package com.cloudkinto.service.order.input.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloudkinto.bo.api.*;
import com.cloudkinto.bo.order.inbound.*;
import com.cloudkinto.bo.order.inbound.jdyOpen.JdyOrderInputAddReq;
import com.cloudkinto.bo.order.inbound.jdyOpen.JdyOrderInputPackageAdd;
import com.cloudkinto.bo.order.inbound.jdyOpen.JdyOrderInputProductAdd;
import com.cloudkinto.bo.order.shelf.ShelfBySkuAddBo;
import com.cloudkinto.bo.order.shelf.ShelfQtyDetailBo;
import com.cloudkinto.bo.storage.StorageLocationQueryBo;
import com.cloudkinto.common.*;
import com.cloudkinto.common.constant.SysConstant;
import com.cloudkinto.common.exception.BizException;
import com.cloudkinto.common.utils.ExcelUtils;
import com.cloudkinto.common.utils.MathUtils;
import com.cloudkinto.common.utils.StringsUtil;
import com.cloudkinto.common.utils.dict.StaticDict;
import com.cloudkinto.common.web.PageResult;
import com.cloudkinto.common.web.SingleResult;
import com.cloudkinto.dao.OrderInputDao;
import com.cloudkinto.dto.order.inbound.*;
import com.cloudkinto.dto.order.returns.OrderReturnPageDto;
import com.cloudkinto.dto.print.common.DynamicTableDto;
import com.cloudkinto.dto.print.common.MultiLevelLoopRowTableDto;
import com.cloudkinto.dto.stock.NormalInputShelfDto;
import com.cloudkinto.dto.stock.TransferInputShelfDto;
import com.cloudkinto.entity.*;
import com.cloudkinto.enumType.common.ProgressEnum;
import com.cloudkinto.enumType.operate.OperateTypeEnum;
import com.cloudkinto.enumType.order.SendInEnum;
import com.cloudkinto.enumType.order.inbound.ArrivalModeEnum;
import com.cloudkinto.enumType.order.inbound.InboundTypeEnum;
import com.cloudkinto.enumType.order.inbound.OrderInputStatusEnum;
import com.cloudkinto.enumType.order.outbound.NormalOutboundStatusEnum;
import com.cloudkinto.enumType.order.shelf.OrderInputShelfStatusEnum;
import com.cloudkinto.enumType.stock.StockOrderTypeEnum;
import com.cloudkinto.enumType.stock.StockTypeEnum;
import com.cloudkinto.service.company.CompanyService;
import com.cloudkinto.service.cost.CostService;
import com.cloudkinto.service.event.order.InboundAddCostEvent;
import com.cloudkinto.service.event.order.PostJdyInputOrderEvent;
import com.cloudkinto.service.file.FileService;
import com.cloudkinto.service.operate.OperateRecordService;
import com.cloudkinto.service.order.box.OrderBoxProductService;
import com.cloudkinto.service.order.box.OrderBoxService;
import com.cloudkinto.service.order.input.OrderInputService;
import com.cloudkinto.service.order.outbound.OutboundService;
import com.cloudkinto.service.order.returns.OrderReturnService;
import com.cloudkinto.service.order.shelf.OrderInputShelfService;
import com.cloudkinto.service.product.ProductService;
import com.cloudkinto.service.progress.ProgressService;
import com.cloudkinto.service.stock.ProductStockService;
import com.cloudkinto.service.storage.StorageLocationService;
import com.cloudkinto.service.system.SystemSetService;
import com.cloudkinto.service.warehouse.WarehouseService;
import com.cloudkinto.utils.AsposePdfUtils;
import com.cloudkinto.utils.BarCodePdfUtils;
import com.cloudkinto.utils.BarCodeUtils;
import com.cloudkinto.utils.BeanCopierUtils;
import com.cloudkinto.vo.api.OrderInputApiListVo;
import com.cloudkinto.vo.api.OrderInputListApiBoxInfoVo;
import com.cloudkinto.vo.common.CommonBatchOperationVo;
import com.cloudkinto.vo.common.ListPageStatusStaDto;
import com.cloudkinto.vo.company.CompanyRes;
import com.cloudkinto.vo.cost.out.CostItemForOrderPageDto;
import com.cloudkinto.vo.order.inbound.*;
import com.cloudkinto.vo.order.outbound.PackageSizeVo;
import com.cloudkinto.vo.storage.StorageLocationVo;
import com.cloudkinto.vo.system.OmsSystemEntity;
import com.cloudkinto.vo.system.omp.OmpSystemEntity;
import com.cloudkinto.vo.system.wms.WmsSystemEntity;
import com.cloudkinto.vo.wr.trial.TrialBoxReq;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.data.*;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.util.PoitlIOUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.itextpdf.text.Document;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 到货通知单（订单入库） service实现类
 * </p>
 *
 * @author long
 * @since 2024-11-04
 */
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
@Slf4j
public class OrderInputServiceImpl extends ServiceImpl<OrderInputDao, OrderInputDo> implements OrderInputService {

    private final WarehouseService warehouseService;
    private final CompanyService companyService;
    private final ProgressService progressService;
    private final OperateRecordService operateRecordService;
    private final OrderBoxService boxService;
    private final OrderBoxProductService boxProductService;
    private final ProductService productService;
    private final OrderInputShelfService shelfService;
    private final RedisTemplate redisTemplate;
    private final ProductStockService productStockService;
    private final OrderReturnService orderReturnService;
    private final SystemSetService systemSetService;
    private final OutboundService outboundService;
    private final FileService fileService;
    private final CostService costService;
    private final RabbitTemplate rabbitTemplate;
    private final ApplicationContext context;
    private final StorageLocationService storageLocationService;

    /**
     * 获取预售在途信息
     *
     * @param map
     * @return
     */
    @Override
    public List<OrderInputProductTransitDto> getTransitProductList(@Param("map") Map<String, Object> map) {
        List<OrderInputProductTransitDto> transitProductList = baseMapper.getTransitProductList(map);
        return transitProductList.stream().filter(transitProduct -> transitProduct.getReservedQty() > 0 && StringUtils.isNotBlank(transitProduct.getSku())).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(OrderInputSaveBo bo, Long userId, String username, Long tenantId, Long companyId) {
        OrderInputDo entityDo = Objects.nonNull(bo.getId()) ? this.getById(bo.getId()) : new OrderInputDo();
//        BeanUtils.copyProperties(bo, entityDo);
        BeanConvert.INSTANCE.inputSaveBoToInputDo(bo, entityDo);
        if (bo.isPostJdyInputOrder()) {
            entityDo.setSendIn(SendInEnum.NotPushed.getValue());
        }
        //新建
        if (Objects.isNull(bo.getId())) {
            entityDo.setCompanyId(companyId);
            entityDo.setTenantId(tenantId);
            entityDo.setReceiptBoxQty(0);
            entityDo.setReceiptPalletsQty(0);
            //生成入库单号
            String orderInputNo = this.generateOrderInputNo(entityDo.getInboundType());
            entityDo.setSourceNo(orderInputNo);
        }
        //计算总箱数
        if (InboundTypeEnum.one.getValue().equals(entityDo.getInboundType())) {
            entityDo.setBoxTotalQty(bo.getBoxList().size());
        }
        if (InboundTypeEnum.two.getValue().equals(entityDo.getInboundType())) {
            entityDo.setBoxTotalQty(
                    bo.getBoxList().stream()
                            .map(OrderInputBoxAddBo::getBoxTotalQty)
                            .reduce(0, Integer::sum)
            );
        }
        if (StrUtil.isNotBlank(bo.getExpectedDate())) {
            entityDo.setExpectedDate(DateUtil.parse(bo.getExpectedDate(), "yyyy-MM-dd"));
        }
        //新增或修改入库单基础数据
        this.saveOrUpdate(entityDo);
        if (Objects.isNull(bo.getId())) {
            //创建入库单进度
            this.createProgress(entityDo, username);
        } else {
            //修改前删除原始记录
            List<OrderBoxDo> boxList = boxService.lambdaQuery().eq(OrderBoxDo::getOrderId, bo.getId()).list();
            if (CollUtil.isNotEmpty(boxList)) {
                List<String> boxMarkNumberList = boxList.stream().map(OrderBoxDo::getBoxMarkNumber).collect(Collectors.toList());
                if (CollUtil.isNotEmpty(boxMarkNumberList)) {
                    boxProductService.remove(Wrappers.<OrderBoxProductDo>lambdaQuery().in(OrderBoxProductDo::getBoxMarkNumber, boxMarkNumberList));
                }
                boxService.remove(Wrappers.<OrderBoxDo>lambdaQuery().eq(OrderBoxDo::getOrderId, bo.getId()));
            }
        }
        List<OrderBoxDo> boxList = new ArrayList<>();
        List<OrderBoxProductDo> boxProductList = new ArrayList<>();
        //操作子表
        bo.getBoxList().forEach(item -> {
            OrderBoxDo boxDo = BeanConvert.INSTANCE.inputBoxAddBoToOrderBoxDo(item);
            if (InboundTypeEnum.one.getValue().equals(bo.getInboundType())) {
                boxDo.setBoxMarkNumber(entityDo.getSourceNo() + "-S" + item.getBoxMarkNumberSeq1() + "-" + item.getBoxMarkNumberSeq2());
            }
            if (InboundTypeEnum.two.getValue().equals(bo.getInboundType())) {
                boxDo.setBoxTypeNumber(item.getBoxTypeNumber());
            }
            boxDo.setOrderId(entityDo.getId());
            boxList.add(boxDo);
            if ((InboundTypeEnum.two.getValue().equals(bo.getInboundType()) && item.isCreateNewBoxTypeNumber()) || InboundTypeEnum.one.getValue().equals(bo.getInboundType())) {
                //批发入库创建新箱号
                List<OrderBoxProductDo> bpList = JSONUtil.toList(JSONUtil.toJsonStr(item.getProductList()), OrderBoxProductDo.class);
                bpList.forEach(product -> {
                    if (InboundTypeEnum.one.getValue().equals(bo.getInboundType())) {
                        product.setBoxMarkNumber(boxDo.getBoxMarkNumber());
                    }
                    if (InboundTypeEnum.two.getValue().equals(bo.getInboundType())) {
                        product.setBoxTypeNumber(boxDo.getBoxTypeNumber());
                    }
                });
                boxProductList.addAll(bpList);
            }
        });
        if (CollUtil.isNotEmpty(boxList)) {
            boxService.batchInsert(boxList);
        }
        if (CollUtil.isNotEmpty(boxProductList)) {
//            List<String> boxTypeNumberList = boxProductList.stream().map(OrderBoxProductDo::getBoxTypeNumber).collect(Collectors.toList());
//            if (CollUtil.isNotEmpty(boxTypeNumberList)) {
//                boxProductService.remove(Wrappers.<OrderBoxProductDo>lambdaQuery().in(OrderBoxProductDo::getBoxTypeNumber, boxTypeNumberList));
//            }
            boxProductService.batchInsert(boxProductList);
        }
        if (StrUtil.isNotBlank(entityDo.getFileIds())) {
            //上传到正式目录
            fileService.uploadFileMult(entityDo.getFileIds().split(","));
        }
        //提交到货通知单后置操作
        if (OrderInputStatusEnum.one.getValue().equals(entityDo.getStatus())) {
            this.pushOrderInputToWmsAfterHandler(entityDo, entityDo.getId(), userId, username, true);
            //推送到筋斗云入库
            if (bo.isPostJdyInputOrder()) {
                context.publishEvent(new PostJdyInputOrderEvent(entityDo, boxList, boxProductList));
            }
        }
        //如果开启了 前置计费
        OmpSystemEntity ompSet = systemSetService.getOmpSet(entityDo.getTenantId());
        if (ompSet.getOmp().getPreBilling() == 1 && (ompSet.getOmp().getBindCompany() == 0 || ompSet.getOmp().getCompany().contains(entityDo.getCompanyId()))) {
//            rabbitTemplate.convertAndSend(RabbitQueueConstant.Shelf_Estimate_Cost, JSON.toJSONString(entityDo));
            context.publishEvent(new InboundAddCostEvent(entityDo, 1));
        }
    }

    /**
     * 创建入库单进度
     * 初始化进度
     */
    private void createProgress(OrderInputDo entityDo, String username) {
        List<ProgressDo> progressDoList = new ArrayList<>();
        progressDoList.add(
                ProgressDo.builder()
                        .type(ProgressEnum.INBOUND.getTypeCode())
                        .bizNumber(entityDo.getId())
                        .bizType("创建")
                        .operateBy(username)
                        .operateTime(DateUtil.date())
                        .sort(1)
                        .build()
        );
        progressDoList.add(
                ProgressDo.builder()
                        .type(ProgressEnum.INBOUND.getTypeCode())
                        .bizNumber(entityDo.getId())
                        .bizType("提交")
                        .operateBy(OrderInputStatusEnum.one.getValue().equals(entityDo.getStatus()) ? username : null)
                        .operateTime(OrderInputStatusEnum.one.getValue().equals(entityDo.getStatus()) ? DateUtil.date() : null)
                        .sort(2)
                        .build()
        );
        progressDoList.add(
                ProgressDo.builder()
                        .type(ProgressEnum.INBOUND.getTypeCode())
                        .bizNumber(entityDo.getId())
                        .bizType("收货")
                        .operateBy(null)
                        .operateTime(null)
                        .sort(3)
                        .build()
        );
        progressDoList.add(
                ProgressDo.builder()
                        .type(ProgressEnum.INBOUND.getTypeCode())
                        .bizNumber(entityDo.getId())
                        .bizType("上架")
                        .operateBy(null)
                        .operateTime(null)
                        .sort(4)
                        .build()
        );
        progressService.batchInsert(progressDoList);
    }

    /**
     * 提交到货通知单后置操作
     */
    private void pushOrderInputToWmsAfterHandler(OrderInputDo inputDo, Long thirdId, Long userId, String username, boolean isCreate) {
        operateRecordService.operateAddRecordAndValue(
                SysConstant.Create_Arrival_Notice,
                "",
                thirdId,
                userId,
                username,
                OperateTypeEnum.OrderInput
        );
        progressService.lambdaUpdate()
                .eq(ProgressDo::getBizNumber, thirdId)
                .eq(ProgressDo::getType, ProgressEnum.INBOUND.getTypeCode())
                .eq(ProgressDo::getBizType, "提交")
                .set(ProgressDo::getOperateBy, username)
                .set(ProgressDo::getOperateTime, DateUtil.date())
                .update();
        //提交时间
        this.lambdaUpdate()
                .eq(OrderInputDo::getId, thirdId)
                .set(OrderInputDo::getPushWmsTime, DateUtil.date())
                .update();
        if (isCreate) {
            return;
        }
        //如果开启了 前置计费
        OmpSystemEntity ompSet = systemSetService.getOmpSet(inputDo.getTenantId());
        if (ompSet.getOmp().getPreBilling() == 1 && (ompSet.getOmp().getBindCompany() == 0 || ompSet.getOmp().getCompany().contains(inputDo.getCompanyId()))) {
//            rabbitTemplate.convertAndSend(RabbitQueueConstant.Shelf_Estimate_Cost, JSON.toJSONString(inputDo));
            context.publishEvent(new InboundAddCostEvent(inputDo, 1));
        }
    }

    /**
     * 生成入库单号
     * 生成规则：IB+入库类型数字+年月日+六位的序列号
     *
     * @param inboundType 入库类型
     */
    private String generateOrderInputNo(Integer inboundType) {
        // 每周重置入库单序号
        //生成入库单号
        String orderInputNoSeq = String.valueOf(redisTemplate.opsForValue().increment(RedisKeyConstants.ORDER_INPUT_NO_SEQ));
        //序列号不足六位在前面补0
        if (orderInputNoSeq.length() < 6) {
            orderInputNoSeq = "000000".substring(0, 6 - orderInputNoSeq.length()) + orderInputNoSeq;
        }
        return "IB"
               + inboundType
               + DateUtil.format(DateUtil.date(), "yyMMdd")
               + orderInputNoSeq;
    }

    @Override
    public void submit(Long id, Long currentUserId, String userName) {
        //将草稿状态改为待入库
        this.lambdaUpdate()
                .eq(OrderInputDo::getId, id)
                .set(OrderInputDo::getStatus, OrderInputStatusEnum.one.getValue())
                .update();
        OrderInputDo inputDo = this.getById(id);
        if (StrUtil.isNotBlank(inputDo.getFileIds())) {
            //上传文件到正式目录
            String[] fileIds = inputDo.getFileIds().split(",");
            fileService.uploadFileMult(fileIds);
        }
        //提交后置操作
        this.pushOrderInputToWmsAfterHandler(inputDo, id, currentUserId, userName, false);
    }

    @Override
    public OrderInputDetailVo detail(Long id) {
        OrderInputDo entityDo = baseMapper.selectById(id);
//        OrderInputDetailVo res = new OrderInputDetailVo();
//        BeanUtils.copyProperties(entityDo, res);
        OrderInputDetailVo res = BeanConvert.INSTANCE.inputDoToOrderInputDetailVo(entityDo);
        //入库类型
        Integer inboundType = entityDo.getInboundType();
        //查询产品
        Collection<OrderBoxDo> boxList = boxService.lambdaQuery()
                .eq(OrderBoxDo::getOrderId, id)
                .list();
        Map<String, OrderBoxDo> boxTypeNoMatchDo = new HashMap<>();
        if (InboundTypeEnum.one.getValue().equals(inboundType)) {
            boxList = new ArrayList<>(
                    boxList.stream()
                            .collect(Collectors.toMap(OrderBoxDo::getBoxMarkNumber, a -> a, (o1, o2) -> {
//                        o1.setBoxTotalQty(o1.getBoxTotalQty() + o2.getBoxTotalQty());
//                        o1.setReceivedBoxQty(o1.getReceivedBoxQty() + o2.getReceivedBoxQty());
                                o1.setSkuTotalQty(o1.getSkuTotalQty() + o2.getSkuTotalQty());
                                o1.setReceivedQty(o1.getReceivedQty() + o2.getReceivedQty());
                                return o1;
                            })).values()
            );
        }
        if (InboundTypeEnum.two.getValue().equals(inboundType)) {
            //备货中转需要合并箱子（多次上架一个箱类型会出现多条数据）
            boxTypeNoMatchDo = boxList.stream()
                    .collect(Collectors.toMap(OrderBoxDo::getBoxTypeNumber, a -> a, (o1, o2) -> {
                        o1.setBoxTotalQty(o1.getBoxTotalQty() + o2.getBoxTotalQty());
                        o1.setReceivedBoxQty(o1.getReceivedBoxQty() + o2.getReceivedBoxQty());
                        o1.setSkuTotalQty(o1.getSkuTotalQty() + o2.getSkuTotalQty());
                        o1.setReceivedQty(o1.getReceivedQty() + o2.getReceivedQty());
                        return o1;
                    }));
            boxList = boxTypeNoMatchDo.values();
        }
        Function<OrderBoxDo, String> function =
                InboundTypeEnum.one.getValue().equals(inboundType)
                        ? OrderBoxDo::getBoxMarkNumber
                        : OrderBoxDo::getBoxTypeNumber;
        SFunction<OrderBoxProductDo, String> tableField =
                InboundTypeEnum.one.getValue().equals(inboundType)
                        ? OrderBoxProductDo::getBoxMarkNumber
                        : OrderBoxProductDo::getBoxTypeNumber;
        List<OrderBoxProductDo> bpList = boxProductService.lambdaQuery()
                .in(tableField, boxList.stream().map(function).collect(Collectors.toSet()))
                .list();
        //查询产品信息
        Set<Long> productIdSet = bpList.stream().map(OrderBoxProductDo::getProductId).collect(Collectors.toSet());
        Map<Long, ProductDo> productIdMatchDo = new HashMap<>();
        if (CollUtil.isNotEmpty(productIdSet)) {
            productIdMatchDo = productService.listByIds(productIdSet).stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        }
        res.setReceiptSkuQty(boxList.stream().map(OrderBoxDo::getReceivedQty).reduce(0, Integer::sum));
        //查询上架数据
        //获取入库单所有上架单id
        List<Long> pasIdList = shelfService.lambdaQuery()
                .eq(OrderInputShelfDo::getInboundSourceNo, res.getSourceNo())
                .list().stream()
                .map(OrderInputShelfDo::getId)
                .collect(Collectors.toList());
        //查询产品上架数据
        Map<Long, List<NormalInputShelfDto>> productIdMatchShelfList = new HashMap<>();
        //查询箱子上架数据
        Map<String, List<TransferInputShelfDto>> boxTypeNumberMatchShelfList = new HashMap<>();
        if (CollUtil.isNotEmpty(pasIdList)) {
            if (InboundTypeEnum.one.getValue().equals(inboundType)) {
                productIdMatchShelfList = productStockService.getNormalInputShelfData(pasIdList).stream().collect(Collectors.groupingBy(NormalInputShelfDto::getProductId));
            }
            if (InboundTypeEnum.two.getValue().equals(inboundType)) {
                boxTypeNumberMatchShelfList = productStockService.getTransferInputShelfData(pasIdList).stream().collect(Collectors.groupingBy(TransferInputShelfDto::getBoxTypeNumber));
            }
        }
        //常规入库添加总上架数
        if (CollUtil.isNotEmpty(productIdMatchShelfList)) {
            res.setGoodQty(
                    productIdMatchShelfList.values().stream()
                            .flatMap(List::stream)
                            .filter(item -> Objects.nonNull(item.getStockAttribute()) && Objects.equals(item.getStockAttribute(), 0))
                            .map(NormalInputShelfDto::getReceiptQty)
                            .reduce(0, Integer::sum)
            );
            res.setBadQty(
                    productIdMatchShelfList.values().stream()
                            .flatMap(List::stream)
                            .filter(item -> Objects.nonNull(item.getStockAttribute()) && Objects.equals(item.getStockAttribute(), 1))
                            .map(NormalInputShelfDto::getReceiptQty)
                            .reduce(0, Integer::sum)
            );
        }
        List<OrderInputProductDetailVo> productDetailVoList = JSONUtil.toList(JSONUtil.toJsonStr(bpList), OrderInputProductDetailVo.class);
        //产品信息补充上架信息
        for (OrderInputProductDetailVo productDetailVo : productDetailVoList) {
            ProductDo productDo = productIdMatchDo.get(productDetailVo.getProductId());
            if (Objects.isNull(productDo)) {
                continue;
            }
            productDetailVo.setProductName(productDo.getProductName());
            productDetailVo.setMainBarcode(productDo.getJanCode());
            //查询sku上架数据
            if (InboundTypeEnum.one.getValue().equals(inboundType)) {
                List<NormalInputShelfDto> shelfDtoList = productIdMatchShelfList.get(productDetailVo.getProductId());
                if (CollUtil.isNotEmpty(shelfDtoList)) {
                    productDetailVo.setGoodQty(
                            shelfDtoList.stream()
                                    .filter(item -> Objects.nonNull(item.getStockAttribute()) && Objects.equals(item.getStockAttribute(), 0))
                                    .map(NormalInputShelfDto::getReceiptQty)
                                    .reduce(0, Integer::sum)
                    );
                    productDetailVo.setBadQty(
                            shelfDtoList.stream()
                                    .filter(item -> Objects.nonNull(item.getStockAttribute()) && Objects.equals(item.getStockAttribute(), 1))
                                    .map(NormalInputShelfDto::getReceiptQty)
                                    .reduce(0, Integer::sum)
                    );
                }
            }
            if (InboundTypeEnum.two.getValue().equals(inboundType)) {
                List<TransferInputShelfDto> shelfDtoList = boxTypeNumberMatchShelfList.get(productDetailVo.getBoxTypeNumber());
                if (CollUtil.isNotEmpty(shelfDtoList)) {
                    productDetailVo.setGoodQty(
                            productDetailVo.getPerBoxQty() * shelfDtoList.stream().mapToInt(TransferInputShelfDto::getReceiptQty).sum()
                    );
                }
            }
        }
        //避免合并相同sku时，影响原数据
        List<OrderInputProductDetailVo> productDetailVoListCopy = JSONUtil.toList(JSONUtil.toJsonStr(productDetailVoList), OrderInputProductDetailVo.class);
        if (InboundTypeEnum.one.getValue().equals(inboundType)) {
            //常规入库简单合并相同sku数据
            Collection<OrderInputProductDetailVo> productVoList = productDetailVoListCopy.stream()
                    .collect(Collectors.toMap(OrderInputProductDetailVo::getSku, a -> a, (o1, o2) -> {
                        o1.setPerBoxQty(o1.getPerBoxQty() + o2.getPerBoxQty());
                        o1.setReceiptQty(o1.getReceiptQty() + o2.getReceiptQty());
                        return o1;
                    })).values();
            res.setProductDetailList(productVoList);
        }
        if (InboundTypeEnum.two.getValue().equals(inboundType)) {
            //中转入库需要乘以箱子数量再进行合并相同sku数据
            for (OrderInputProductDetailVo item : productDetailVoListCopy) {
                OrderBoxDo boxDo = boxTypeNoMatchDo.get(item.getBoxTypeNumber());
                item.setReceiptQty(item.getPerBoxQty() * boxDo.getReceivedBoxQty());
                item.setPerBoxQty(item.getPerBoxQty() * boxDo.getBoxTotalQty());
            }
            Collection<OrderInputProductDetailVo> productVoList = productDetailVoListCopy.stream()
                    .collect(Collectors.toMap(OrderInputProductDetailVo::getSku, a -> a, (o1, o2) -> {
                        o1.setPerBoxQty(o1.getPerBoxQty() + o2.getPerBoxQty());
                        o1.setReceiptQty(o1.getReceiptQty() + o2.getReceiptQty());
                        o1.setGoodQty(o1.getGoodQty() + o2.getGoodQty());
                        return o1;
                    })).values();
            res.setProductDetailList(productVoList);
        }
        Collection<OrderInputBoxDetailVo> boxDetailVoList = JSONUtil.toList(JSONUtil.toJsonStr(boxList), OrderInputBoxDetailVo.class);
        //为箱子添加产品信息
        for (OrderInputBoxDetailVo boxDetailVo : boxDetailVoList) {
            boxDetailVo.setProductDetailList(
                    productDetailVoList.stream()
                            .filter(productDetailVo -> {
                                if (InboundTypeEnum.one.getValue().equals(inboundType)) {
                                    return Objects.equals(productDetailVo.getBoxMarkNumber(), boxDetailVo.getBoxMarkNumber());
                                }
                                if (InboundTypeEnum.two.getValue().equals(inboundType)) {
                                    return Objects.equals(productDetailVo.getBoxTypeNumber(), boxDetailVo.getBoxTypeNumber());
                                }
                                return false;
                            })
                            .collect(Collectors.toList())
            );
            if (InboundTypeEnum.one.getValue().equals(inboundType)) {
                boxDetailVo.setBoxMarkNumber1(boxDetailVo.getBoxMarkNumber().split("-")[1]);
                boxDetailVo.setBoxMarkNumber2(Integer.valueOf(boxDetailVo.getBoxMarkNumber().split("-")[2]));
            }
            if (InboundTypeEnum.two.getValue().equals(inboundType)) {
                List<TransferInputShelfDto> shelfDtoList = boxTypeNumberMatchShelfList.get(boxDetailVo.getBoxTypeNumber());
                if (CollUtil.isNotEmpty(shelfDtoList)) {
                    boxDetailVo.setShelfQty(shelfDtoList.stream().mapToInt(TransferInputShelfDto::getReceiptQty).sum());
                }
//                boxDetailVo.setBoxTypeNumberSeq(boxDetailVo.getBoxTypeNumber().split("-")[1]);
                //计算体积 单位：立方厘米
                BigDecimal receiptVolume = boxDetailVo.getLength()
                        .multiply(boxDetailVo.getWidth())
                        .multiply(boxDetailVo.getHeight())
                        .multiply(BigDecimal.valueOf(boxDetailVo.getReceivedBoxQty()));
                BigDecimal volumeTotal = boxDetailVo.getLength()
                        .multiply(boxDetailVo.getWidth())
                        .multiply(boxDetailVo.getHeight())
                        .multiply(BigDecimal.valueOf(boxDetailVo.getBoxTotalQty()));
                res.setVolumeTotal(res.getVolumeTotal().add(volumeTotal));
                res.setReceiptVolume(res.getReceiptVolume().add(receiptVolume));
                //计算重量
                BigDecimal receiptWeight = boxDetailVo.getWeight()
                        .multiply(BigDecimal.valueOf(boxDetailVo.getReceivedBoxQty()))
                        .setScale(3, RoundingMode.HALF_UP);
                BigDecimal weightTotal = boxDetailVo.getWeight()
                        .multiply(BigDecimal.valueOf(boxDetailVo.getBoxTotalQty()))
                        .setScale(3, RoundingMode.HALF_UP);
                res.setWeightTotal(res.getWeightTotal().add(weightTotal));
                res.setReceiptWeight(res.getReceiptWeight().add(receiptWeight));
            }
        }
        res.setBoxDetailList(boxDetailVoList);
        //体积转换 立方厘米转立方米
        BigDecimal receiptVolume = res.getReceiptVolume().divide(BigDecimal.valueOf(1000000), 6, RoundingMode.HALF_UP);
        //体积不足0.001立方米按0.001立方米计算
        if (receiptVolume.compareTo(BigDecimal.valueOf(0.001)) > 0) {
            receiptVolume = receiptVolume.setScale(3, RoundingMode.HALF_UP);
        } else if (receiptVolume.compareTo(BigDecimal.ZERO) > 0) {
            receiptVolume = BigDecimal.valueOf(0.001);
        } else if (receiptVolume.compareTo(BigDecimal.ZERO) <= 0) {
            receiptVolume = BigDecimal.ZERO;
        }
        BigDecimal volumeTotal = res.getVolumeTotal().divide(BigDecimal.valueOf(1000000), 6, RoundingMode.HALF_UP);
        if (volumeTotal.compareTo(BigDecimal.valueOf(0.001)) > 0) {
            volumeTotal = volumeTotal.setScale(3, RoundingMode.HALF_UP);
        } else if (volumeTotal.compareTo(BigDecimal.ZERO) > 0) {
            volumeTotal = BigDecimal.valueOf(0.001);
        } else if (volumeTotal.compareTo(BigDecimal.ZERO) <= 0) {
            volumeTotal = BigDecimal.ZERO;
        }
        res.setReceiptVolume(receiptVolume);
        res.setVolumeTotal(volumeTotal);
        return res;
    }

    @Override
    public SingleResult<Map<String, Object>> pageInit(Long companyId, Long tenantId, String source, Long userId) {
        HashMap<String, Object> map = new HashMap<>(8);
        if ("oms".equals(source)) {
            map.put("warehouseList", warehouseService.getWarehouseCompany(companyId, tenantId));
        } else {
            map.put("companyList", companyService.getCompanyList(userId, tenantId, source));
            map.put("warehouseList", warehouseService.getList(tenantId));
        }
        map.put("arrivalMode", ArrivalModeEnum.getArrivalModeList());
        map.put("inboundType", InboundTypeEnum.getInboundTypeList());
        map.put("statusList", OrderInputStatusEnum.getInputOrderStatusList());
        return SingleResult.success(map);
    }

    @Override
    public void updateExpectedDate(Long id, String expectedDate, Long userId, String username) {
        OrderInputDo orderInputDo = baseMapper.selectById(id);
        if (!OrderInputStatusEnum.one.getValue().equals(orderInputDo.getStatus())) {
            throw new BizException(SysConstant.Status_Not_Modified_Arrival_Time);
        }
        this.lambdaUpdate()
                .eq(OrderInputDo::getId, id)
                .set(OrderInputDo::getExpectedDate, DateUtil.parse(expectedDate))
                .update();
        operateRecordService.operateAddRecordAndValue(
                SysConstant.OrderInput_Update_Expected_Date,
                "",
                id,
                userId,
                username,
                OperateTypeEnum.OrderInput
        );
    }

    @Override
    public List<OrderScanResultVo> scan(String condition, Long whId, Long tenantId) {
        List<OrderScanResultVo> list = new ArrayList<>();
        //查询入库单
        List<OrderScanResultVo> orderInputList = baseMapper.scan(condition);
        if (CollUtil.isNotEmpty(orderInputList)) {
            list.addAll(orderInputList);
        }
        //退件扫描时，扫描到已出库的一件代发出库单的跟踪号，将识别为物流商退件。
        // 如勾选此按钮，将不再识别为物流商退件，会直接创建认领单
        WmsSystemEntity wmsSet = systemSetService.getWmsSet(whId);
        if (wmsSet.getWms().getBack().getReturnLogistics() == 0) {
            //将近三个月已出库得一件代发出库 通过快递单号 匹配上
            List<OrderOutboundDo> outboundDoList = outboundService.list(Wrappers.<OrderOutboundDo>lambdaQuery()
                    .eq(OrderOutboundDo::getTenantId, tenantId)
                    .eq(OrderOutboundDo::getOrderType, 1)
                    .eq(OrderOutboundDo::getWhId, whId)
                    .ge(OrderOutboundDo::getCreateTime, DateUtil.offset(DateUtil.date(), DateField.MONTH, -3))
                    .eq(OrderOutboundDo::getTrackingNumber, condition)
                    .eq(OrderOutboundDo::getStatus, NormalOutboundStatusEnum.five.getValue())
                    .select(OrderOutboundDo::getId,
                            OrderOutboundDo::getCompanyId,
                            OrderOutboundDo::getTrackingNumber,
                            OrderOutboundDo::getOutboundNo)
            );
            if (CollUtil.isNotEmpty(outboundDoList)) {
                Map<Long, CompanyDo> companyDoMap = companyService.listByIds(outboundDoList.stream().map(OrderOutboundDo::getCompanyId).collect(Collectors.toSet())).stream()
                        .collect(Collectors.toMap(CompanyDo::getId, val -> val));
                for (OrderOutboundDo outboundDo : outboundDoList) {
                    list.add(
                            OrderScanResultVo.builder()
                                    .id(outboundDo.getId())
                                    .orderNo(outboundDo.getOutboundNo())
                                    .type("服务商退件")
                                    .companyName(companyDoMap.get(outboundDo.getCompanyId()).getName())
                                    .companyCode(companyDoMap.get(outboundDo.getCompanyId()).getCompanyCode())
                                    .build()
                    );
                }
            }
        }
        //勾选了退件扫描不自动识别物流商退件 如果扫描到退件单 则不需要创建认领单
        if (StringUtils.isNotBlank(condition)) {
            Map<String, Object> queryMap = new HashMap<>();
            queryMap.put("checkStatus", 2);
            queryMap.put("tenantId", tenantId);
            queryMap.put("scanNumber", condition);
            queryMap.put("whId", whId);
            List<OrderReturnPageDto> orderReturnList = orderReturnService.getList(queryMap);
            list.addAll(BeanConvert.INSTANCE.orderScanResultDto(orderReturnList));
        }


        return list;
    }

    @Override
    public PageResult acceptGoodsListPage(AcceptGoodsQueryBo bo) {
        Integer current = bo.getCurrent();
        Integer pageSize = bo.getPageSize();
        PageHelper.startPage(current, pageSize);
        List<OrderInputAcceptGoodsBoxListVo> list = baseMapper.acceptGoodsListPage(bo);
        PageInfo pageInfo = new PageInfo(list);
        return new PageResult(
                this.acceptGoodsListPageAfterHandler(list),
                current,
                pageSize,
                (int) pageInfo.getTotal(),
                pageInfo.getPages()
        );
    }

    /**
     * 处理箱子内产品数据
     */
    private List<OrderInputAcceptGoodsBoxListVo> acceptGoodsListPageAfterHandler(List<OrderInputAcceptGoodsBoxListVo> listVos) {
        if (CollUtil.isEmpty(listVos)) {
            return listVos;
        }
        List<String> boxMarkNumberList = listVos.stream()
                .map(OrderInputAcceptGoodsBoxListVo::getBoxMarkNumber)
                .filter(StringUtils::isNotEmpty)
                .collect(Collectors.toList());
        List<String> boxTypeNumberList = listVos.stream()
                .map(OrderInputAcceptGoodsBoxListVo::getBoxTypeNumber)
                .filter(StringUtils::isNotEmpty)
                .collect(Collectors.toList());
        SFunction<OrderBoxProductDo, String> tableField = CollUtil.isNotEmpty(boxMarkNumberList)
                ? OrderBoxProductDo::getBoxMarkNumber
                : OrderBoxProductDo::getBoxTypeNumber;
//        Function<OrderBoxDo, String> function =
//                InboundTypeEnum.one.getValue().equals(orderInputDo.getInboundType())
//                        ? OrderBoxDo::getBoxMarkNumber
//                        : OrderBoxDo::getBoxTypeNumber;
        //查询箱子内产品
        List<OrderBoxProductDo> bpList = boxProductService.lambdaQuery()
                .select(
                        OrderBoxProductDo::getSku,
                        OrderBoxProductDo::getProductId,
                        OrderBoxProductDo::getReceiptQty,
                        OrderBoxProductDo::getPerBoxQty,
                        OrderBoxProductDo::getBoxMarkNumber,
                        OrderBoxProductDo::getBoxTypeNumber
                )
                .in(tableField, CollUtil.isNotEmpty(boxMarkNumberList) ? boxMarkNumberList : boxTypeNumberList)
                .apply(CollUtil.isNotEmpty(boxMarkNumberList), "receipt_qty < per_box_qty")
                .list();
        //查询涉及到的产品信息
        List<Long> productIdList = bpList.stream().map(OrderBoxProductDo::getProductId).distinct().collect(Collectors.toList());
        Map<Long, ProductDo> productIdMatch = new HashMap<>();
        if (CollUtil.isNotEmpty(productIdList)) {
            productIdMatch = productService.listByIds(productIdList).stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        }
        //根据箱唛号/箱类型号进行分组
        Map<String, List<OrderBoxProductDo>> boxMatchProductList = bpList.stream()
                .collect(Collectors.groupingBy(tableField));
        //往箱子组装产品数据
        for (OrderInputAcceptGoodsBoxListVo vo : listVos) {
            List<OrderBoxProductDo> boxProductDoList = boxMatchProductList.get(
                    CollUtil.isNotEmpty(boxMarkNumberList) ? vo.getBoxMarkNumber() : vo.getBoxTypeNumber()
            );
            if (CollUtil.isNotEmpty(boxMarkNumberList)) {
                boxProductDoList.forEach(item -> item.setPerBoxQty(item.getPerBoxQty() - item.getReceiptQty()));
            }
            List<OrderInputAcceptGoodsBpListVo> acceptGoodsBpListVos = JSONUtil.toList(JSONUtil.toJsonStr(boxProductDoList), OrderInputAcceptGoodsBpListVo.class);
            for (OrderInputAcceptGoodsBpListVo bpVo : acceptGoodsBpListVos) {
                ProductDo productDo = productIdMatch.get(bpVo.getProductId());
                if (productDo == null) {
                    continue;
                }
                bpVo.setBarcode(productDo.getJanCode());
                bpVo.setProductName(productDo.getProductName());
            }
            vo.setSkuList(acceptGoodsBpListVos);
        }
        return listVos;
    }

    @Override
    public OrderInputAcceptGoodsOrderDetailVo acceptGoodsDetail(AcceptGoodsDetailQueryBo bo) {
        if (StrUtil.isBlank(bo.getSourceNo())
            && StrUtil.isBlank(bo.getBoxMarkNumber())
            /*&& StrUtil.isBlank(bo.getBoxTypeNumber())*/) {
            throw new BizException(SysConstant.Query_Condition_Empty);
        }
        OrderInputAcceptGoodsOrderDetailVo dto = baseMapper.acceptGoodsDetail(bo);
        if (Objects.isNull(dto)) {
            throw new BizException(SysConstant.Inbound_Not_Exist);
        }
        return dto;
    }

    @Override
    public SingleResult<Map<String, Object>> acceptGoodsDetailInit() {
        HashMap<String, Object> map = new HashMap<>(4);
        map.put("inboundType", InboundTypeEnum.getInboundTypeList());
        map.put("statusList", OrderInputStatusEnum.getInputOrderStatusList());
        return SingleResult.success(map);
    }

    @Override
    public String receiptTotalById(Long id, Long userId, String username) {
        OrderInputDo orderInputDo = baseMapper.selectOne(
                Wrappers.<OrderInputDo>lambdaQuery().eq(OrderInputDo::getId, id)
        );
        return receiptTotalHandler(id, userId, username, orderInputDo);
    }

    @Override
    public String receiptTotalHandler(Long id, Long userId, String username, OrderInputDo orderInputDo) {
        if (Objects.isNull(orderInputDo)) {
            throw new BizException(SysConstant.Inbound_Not_Exist);
        }
        if (!OrderInputStatusEnum.one.getValue().equals(orderInputDo.getStatus()) && !OrderInputStatusEnum.two.getValue().equals(orderInputDo.getStatus())) {
            throw new BizException(SysConstant.OrderInput_Receipt_Status_Error);
        }
        if (Objects.equals(Boolean.TRUE, orderInputDo.getApiSkuAcceptFlag())) {
            throw new BizException(SysConstant.OrderInput_Api_Sku_Accept_Error);
        }
        //生成上架单记录
        String shelfNo = this.addShelfRecord(
                orderInputDo,
                userId,
                username
        ).getPasNo();
        //查询待收货的箱子
        List<OrderBoxDo> boxList = boxService.lambdaQuery()
                .eq(OrderBoxDo::getOrderId, id)
                //避免已收货的箱子重复收货
                .isNull(OrderBoxDo::getReceivedTime)
                .list();
        Function<OrderBoxDo, String> function =
                InboundTypeEnum.one.getValue().equals(orderInputDo.getInboundType())
                        ? OrderBoxDo::getBoxMarkNumber
                        : OrderBoxDo::getBoxTypeNumber;
        SFunction<OrderBoxProductDo, String> function1 =
                InboundTypeEnum.one.getValue().equals(orderInputDo.getInboundType())
                        ? OrderBoxProductDo::getBoxMarkNumber
                        : OrderBoxProductDo::getBoxTypeNumber;
        List<OrderBoxProductDo> bpList = boxProductService.lambdaQuery()
                .in(function1, boxList.stream().map(function).collect(Collectors.toList()))
                .list();
        boxList.forEach(box -> {
            box.setReceivedTime(DateUtil.date());
            box.setReceivedQty(box.getSkuTotalQty());
            if (InboundTypeEnum.two.getValue().equals(orderInputDo.getInboundType())) {
                box.setReceivedBoxQty(box.getBoxTotalQty());
            }
            //设置上架单号
            box.setPasNo(shelfNo);
        });
        boxService.updateBatchById(boxList);
        bpList.forEach(bp -> bp.setReceiptQty(bp.getPerBoxQty()));
        boxProductService.updateBatchById(bpList);
        this.lambdaUpdate()
                .eq(OrderInputDo::getId, id)
                .set(OrderInputDo::getReceiptBoxQty, orderInputDo.getBoxTotalQty())
                .update();
        //收货完成
        this.receiptFinish(id, userId, username, false);
        return shelfNo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean normalReceiptConfirm(NormalReceiptConfirmBo bo, Long userId, String username) {
        OrderInputDo orderInputDo = baseMapper.selectOne(
                Wrappers.<OrderInputDo>lambdaQuery().eq(OrderInputDo::getSourceNo, bo.getSourceNo())
        );
        if (Objects.isNull(orderInputDo)) {
            throw new BizException(SysConstant.Inbound_Not_Exist);
        }
        //判断入库单状态 不为待入库和入库中直接返回
        if (!OrderInputStatusEnum.one.getValue().equals(orderInputDo.getStatus()) &&
            !OrderInputStatusEnum.two.getValue().equals(orderInputDo.getStatus())) {
            return true;
        }
        if (Objects.equals(Boolean.TRUE, orderInputDo.getApiSkuAcceptFlag())) {
            throw new BizException(SysConstant.OrderInput_Api_Sku_Accept_Error);
        }
        Long inputDoId = orderInputDo.getId();
        //修改入库单实收托盘数
        Integer palletQty = bo.getPalletQty();
        if (Objects.nonNull(palletQty) && palletQty > 0) {
            this.lambdaUpdate()
                    .eq(OrderInputDo::getId, inputDoId)
                    .set(OrderInputDo::getReceiptPalletsQty, palletQty + orderInputDo.getReceiptPalletsQty())
                    .update();
            //修改上架单实收托盘数日志
            this.updatePalletQtyLog(inputDoId, userId, username);
        }
        //生成上架单记录
        String shelfNo = this.addShelfRecord(
                orderInputDo,
                userId,
                username
        ).getPasNo();
        //查询待收货的箱子
        int waitReceiptBoxQty = boxService.lambdaQuery()
                .eq(OrderBoxDo::getOrderId, inputDoId)
                //避免已收货的箱子重复收货
                .isNull(OrderBoxDo::getReceivedTime)
                .count();
        List<NormalReceiptConfirmDetailBo> detailList = bo.getDetailList();
        if (CollUtil.isNotEmpty(detailList)) {
            DateTime receivedTime = DateUtil.date();
            for (NormalReceiptConfirmDetailBo detailBo : detailList) {
                //修改箱子内产品实收数量
                detailBo.getSkuList().forEach(sku -> {
                    boxProductService.lambdaUpdate()
                            .eq(OrderBoxProductDo::getBoxMarkNumber, detailBo.getBoxMarkNumber())
                            .eq(OrderBoxProductDo::getSku, sku.getSku())
//                            .set(OrderBoxProductDo::getReceiptQty, sku.getReceiptQty())
                            .setSql("receipt_qty = receipt_qty + " + sku.getReceiptQty())
                            .update();
                });
                //修改箱子实收产品数量及收货时间
                boxService.lambdaUpdate()
                        .eq(OrderBoxDo::getBoxMarkNumber, detailBo.getBoxMarkNumber())
                        .isNull(OrderBoxDo::getReceivedTime)
                        .set(OrderBoxDo::getReceivedTime, receivedTime)
                        .set(
                                OrderBoxDo::getReceivedQty,
                                detailBo.getSkuList().stream()
                                        .map(NormalReceiptConfirmDetailSkuBo::getReceiptQty)
                                        .reduce(0, Integer::sum)
                        )
                        .set(OrderBoxDo::getPasNo, shelfNo)
                        .update();
            }
            //修改入库单实收箱数及状态
            this.lambdaUpdate()
                    .eq(OrderInputDo::getId, inputDoId)
                    .set(OrderInputDo::getReceiptBoxQty, orderInputDo.getReceiptBoxQty() + detailList.size())
                    .set(OrderInputDo::getStatus, OrderInputStatusEnum.two.getValue())
                    .update();
            //日志收货完成
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.OrderInput_Part_Receipt,
                    "",
                    inputDoId,
                    userId,
                    username,
                    OperateTypeEnum.OrderInput
            );
            if (waitReceiptBoxQty == detailList.size()) {
                //收货完成
                this.receiptFinish(inputDoId, userId, username, false);
                return true;
            }
        }
        return false;
    }

    @Override
    public void normalReceiptAndShelfApi(NormalReceiptAndShelfApiBo bo, Long userId, String username) {
        //校验参数
        if (StrUtil.isBlank(bo.getSourceNo())) {
            throw new BizException(SysConstant.SourceNo_Not_Null);
        }
        if (CollUtil.isEmpty(bo.getDetailList())) {
            throw new BizException(SysConstant.Receipt_Detail_Not_Null);
        }
        OrderInputDo inputDo = baseMapper.selectOne(
                Wrappers.<OrderInputDo>lambdaQuery().eq(OrderInputDo::getSourceNo, bo.getSourceNo())
        );
        if (Objects.isNull(inputDo)) {
            throw new BizException(SysConstant.Inbound_Not_Exist);
        }
        //判断入库单状态 不为待入库和入库中直接返回
        if (!OrderInputStatusEnum.one.getValue().equals(inputDo.getStatus()) &&
            !OrderInputStatusEnum.two.getValue().equals(inputDo.getStatus())) {
            throw new BizException(SysConstant.OrderInput_Receipt_Status_Error);
        }
        //判断入库单类型是否为常规入库单
        if (!InboundTypeEnum.one.getValue().equals(inputDo.getInboundType())) {
            throw new BizException(SysConstant.OrderInput_Type_Error);
        }
        if (Objects.equals(Boolean.TRUE, inputDo.getApiSkuAcceptFlag())) {
            throw new BizException(SysConstant.OrderInput_Api_Sku_Accept_Error);
        }
        Long inputDoId = inputDo.getId();
        //生成上架单记录
        OrderInputShelfDo shelfDo = this.addShelfRecord(
                inputDo,
                userId,
                username
        );
        List<OrderBoxDo> boxDoList = boxService.lambdaQuery()
                .eq(OrderBoxDo::getOrderId, inputDoId)
                //避免已收货的箱子重复收货
                .isNull(OrderBoxDo::getReceivedTime)
                .list();
        Set<String> boxMarkNumberSet = boxDoList.stream().map(OrderBoxDo::getBoxMarkNumber).collect(Collectors.toSet());
        //查询待收货的箱子
        int waitReceiptBoxQty = boxDoList.size();
        List<NormalReceiptAndShelfDetailApiBo> detailList = bo.getDetailList();
        List<NormalReceiptAndShelfDetailSkuApiBo> skuApiBoList = new ArrayList<>();
        DateTime receivedTime = DateUtil.date();
        for (NormalReceiptAndShelfDetailApiBo detailBo : detailList) {
            if (!boxMarkNumberSet.contains(detailBo.getBoxMarkNumber())) {
                throw new BizException(SysConstant.Inbound_Not_Exist_Box, inputDo.getSourceNo(), detailBo.getBoxMarkNumber());
            }
            //修改箱子内产品实收数量
            for (NormalReceiptAndShelfDetailSkuApiBo skuApiBo : detailBo.getSkuList()) {
                if (CollUtil.isEmpty(skuApiBo.getDetailList())) {
                    continue;
                }
                boolean update = boxProductService.lambdaUpdate()
                        .eq(OrderBoxProductDo::getBoxMarkNumber, detailBo.getBoxMarkNumber())
                        .eq(OrderBoxProductDo::getSku, skuApiBo.getSku())
                        .set(OrderBoxProductDo::getReceiptQty, skuApiBo.getDetailList().stream().map(ShelfQtyDetailApiBo::getReceiptQty).reduce(0, Integer::sum))
                        .update();
                if (!update) {
                    throw new BizException(SysConstant.Company_Not_Exist_Sku, "", skuApiBo.getSku());
                }
                skuApiBoList.add(skuApiBo);
            }
            //修改箱子实收产品数量及收货时间
            boxService.lambdaUpdate()
                    .eq(OrderBoxDo::getBoxMarkNumber, detailBo.getBoxMarkNumber())
                    .set(OrderBoxDo::getReceivedTime, receivedTime)
                    .set(
                            OrderBoxDo::getReceivedQty,
                            detailBo.getSkuList().stream()
                                    .map(NormalReceiptAndShelfDetailSkuApiBo::getDetailList)
                                    .flatMap(List::stream)
                                    .map(ShelfQtyDetailApiBo::getReceiptQty)
                                    .reduce(0, Integer::sum)
                    )
                    .set(OrderBoxDo::getPasNo, shelfDo.getPasNo())
                    .update();
        }
        //修改入库单实收箱数及状态
        this.lambdaUpdate()
                .eq(OrderInputDo::getId, inputDoId)
                .set(OrderInputDo::getReceiptBoxQty, inputDo.getReceiptBoxQty() + detailList.size())
                .set(OrderInputDo::getStatus, OrderInputStatusEnum.two.getValue())
                .update();
        //日志收货完成
        operateRecordService.operateAddRecordAndValue(
                SysConstant.OrderInput_Part_Receipt,
                "",
                inputDoId,
                userId,
                username,
                OperateTypeEnum.OrderInput
        );
        if (waitReceiptBoxQty == detailList.size()) {
            //收货完成
            this.receiptFinish(inputDoId, userId, username, false);
        }
        apiShelfHandler(userId, username, inputDo, shelfDo, skuApiBoList);
    }

    @Override
    public void normalReceiptAndShelfBySkuApi(NormalReceiptAndShelfBySkuApiBo bo, Long userId, String username) {
        //校验参数
        if (StrUtil.isBlank(bo.getSourceNo())) {
            throw new BizException(SysConstant.SourceNo_Not_Null);
        }
        if (CollUtil.isEmpty(bo.getSkuList())) {
            throw new BizException(SysConstant.Receipt_Detail_Not_Null);
        }
        OrderInputDo inputDo = baseMapper.selectOne(
                Wrappers.<OrderInputDo>lambdaQuery().eq(OrderInputDo::getSourceNo, bo.getSourceNo())
        );
        if (Objects.isNull(inputDo)) {
            throw new BizException(SysConstant.Inbound_Not_Exist);
        }
        //判断入库单状态 不为待入库和收货中直接返回
        if (!OrderInputStatusEnum.one.getValue().equals(inputDo.getStatus()) &&
            !OrderInputStatusEnum.two.getValue().equals(inputDo.getStatus())) {
            throw new BizException(SysConstant.OrderInput_Receipt_Status_Error);
        }
        //判断入库单类型是否为常规入库单
        if (!InboundTypeEnum.one.getValue().equals(inputDo.getInboundType())) {
            throw new BizException(SysConstant.OrderInput_Type_Error);
        }
        Long inputDoId = inputDo.getId();
        //生成上架单记录
        OrderInputShelfDo shelfDo = this.addShelfRecord(inputDo, userId, username);
        //上架产品信息
        List<NormalReceiptAndShelfDetailSkuApiBo> skuApiBoList = bo.getSkuList();
        List<OrderBoxDo> boxDoList = boxService.lambdaQuery()
                .eq(OrderBoxDo::getOrderId, inputDoId)
                //避免已收货的箱子重复收货
                .isNull(OrderBoxDo::getReceivedTime)
                .list();
        if (CollUtil.isEmpty(boxDoList)) {
            throw new BizException(SysConstant.Order_Error);
        }
        List<OrderBoxProductDo> boxProductDoList = boxProductService.lambdaQuery()
                .in(OrderBoxProductDo::getBoxMarkNumber, boxDoList.stream().map(OrderBoxDo::getBoxMarkNumber).collect(Collectors.toList()))
                .list();
        Map<String, Integer> boxMarkShelfQtyMap = new HashMap<>();
        for (NormalReceiptAndShelfDetailSkuApiBo skuApiBo : skuApiBoList) {
            String sku = skuApiBo.getSku();
            List<ShelfQtyDetailApiBo> detailList = skuApiBo.getDetailList();
            if (CollUtil.isEmpty(detailList)) {
                throw new BizException(SysConstant.Receipt_Detail_Not_Null);
            }
            Integer shelfQty = detailList.stream().map(ShelfQtyDetailApiBo::getReceiptQty).filter(Objects::nonNull).reduce(0, Integer::sum);
            List<OrderBoxProductDo> bpListBySku = boxProductDoList.stream().filter(item -> Objects.equals(item.getSku(), sku)).collect(Collectors.toList());
            if (CollUtil.isEmpty(bpListBySku)) {
                throw new BizException(SysConstant.Company_Not_Exist_Sku, "", sku);
            }
            AtomicInteger needShelfQty = new AtomicInteger(shelfQty);
            for (int index = 0, bpListBySkuSize = bpListBySku.size(); index < bpListBySkuSize; index++) {
                if (needShelfQty.get() <= 0) {
                    break;
                }
                boolean last = index == bpListBySkuSize - 1;
                OrderBoxProductDo boxProductDo = bpListBySku.get(index);
                if (boxProductDo.getReceiptQty() < boxProductDo.getPerBoxQty() || last) {
                    int quantity = boxProductDo.getPerBoxQty() - boxProductDo.getReceiptQty();
                    int qty = 0;
                    if (quantity >= needShelfQty.get() || last) {
                        qty = needShelfQty.get();
                        needShelfQty.set(0);
                    } else if (quantity < needShelfQty.get()) {
                        qty = quantity;
                        needShelfQty.set(needShelfQty.get() - quantity);
                    }
                    boxMarkShelfQtyMap.put(boxProductDo.getBoxMarkNumber(), boxMarkShelfQtyMap.getOrDefault(boxProductDo.getBoxMarkNumber(), 0) + qty);
                    boxProductService.lambdaUpdate()
                            .set(OrderBoxProductDo::getReceiptQty, boxProductDo.getReceiptQty() + qty)
                            .eq(OrderBoxProductDo::getId, boxProductDo.getId())
                            .update();
                }
            }
        }
        int receiptBoxQty = 0;
        if (CollUtil.isNotEmpty(boxMarkShelfQtyMap)) {
            for (OrderBoxDo boxDo : boxDoList) {
                Integer shelfQty = boxMarkShelfQtyMap.get(boxDo.getBoxMarkNumber());
                if (Objects.nonNull(shelfQty)) {
                    Integer receivedQty = boxDo.getReceivedQty();
                    Integer skuTotalQty = boxDo.getSkuTotalQty();
                    if (shelfQty + receivedQty >= skuTotalQty) {
                        boxService.lambdaUpdate()
                                .eq(OrderBoxDo::getId, boxDo.getId())
                                .set(OrderBoxDo::getReceivedQty, shelfQty + receivedQty)
                                .set(OrderBoxDo::getReceivedTime, DateUtil.date())
                                .set(OrderBoxDo::getPasNo, shelfDo.getPasNo())
                                .update();
                        receiptBoxQty++;
                    } else {
//                        OrderBoxDo copy = new OrderBoxDo();
//                        BeanUtils.copyProperties(boxDo, copy);
                        OrderBoxDo copy = BeanConvert.INSTANCE.copyOrderBoxDo(boxDo);
                        copy.setId(null);
                        copy.setReceivedQty(shelfQty + receivedQty);
                        copy.setSkuTotalQty(shelfQty + receivedQty);
                        copy.setReceivedTime(DateUtil.date());
                        copy.setPasNo(shelfDo.getPasNo());
                        boxService.save(copy);
                        boxService.lambdaUpdate()
                                .eq(OrderBoxDo::getId, boxDo.getId())
                                .set(OrderBoxDo::getSkuTotalQty, skuTotalQty - shelfQty - receivedQty)
                                .set(OrderBoxDo::getReceivedQty, 0)
                                .update();
                    }
                }
            }
        }
        //修改入库单实收箱数及状态
        this.lambdaUpdate()
                .eq(OrderInputDo::getId, inputDoId)
                .set(OrderInputDo::getReceiptBoxQty, inputDo.getReceiptBoxQty() + receiptBoxQty)
                .set(OrderInputDo::getStatus, OrderInputStatusEnum.two.getValue())
                .set(OrderInputDo::getApiSkuAcceptFlag, true)
                .update();
        //日志收货完成
        operateRecordService.operateAddRecordAndValue(
                SysConstant.OrderInput_Part_Receipt,
                "",
                inputDoId,
                userId,
                username,
                OperateTypeEnum.OrderInput
        );
        if (boxDoList.size() == receiptBoxQty) {
            //收货完成
            this.receiptFinish(inputDoId, userId, username, false);
        }
        apiShelfHandler(userId, username, inputDo, shelfDo, skuApiBoList);
    }

    @Override
    public void apiShelfHandler(Long userId, String username, OrderInputDo inputDo, OrderInputShelfDo shelfDo, List<NormalReceiptAndShelfDetailSkuApiBo> skuApiBoList) {
        if (CollUtil.isNotEmpty(skuApiBoList)) {
            StorageLocationQueryBo slBo = new StorageLocationQueryBo();
            slBo.setTenantId(inputDo.getTenantId());
            slBo.setWhId(inputDo.getWarehouseId());
            Collection<StorageLocationVo> cellList = storageLocationService.getListPage(slBo).getData();
            if (CollUtil.isEmpty(cellList)) {
                operateRecordService.operateAddRecordAndValue(
                        SysConstant.Rack_NotExist,
                        "",
                        inputDo.getId(),
                        userId,
                        username,
                        OperateTypeEnum.OrderInput
                );
                throw new BizException(SysConstant.Rack_NotExist);
            }
            //从正/次库位中取一个
            Map<Integer, Long> stockAttributeMatchCellId = cellList.stream().collect(Collectors.toMap(StorageLocationVo::getMainType, StorageLocationVo::getId, (v1, v2) -> v1));
            //sku合并 上架库存属性一致的数量进行合并
            skuApiBoList = new ArrayList<>(skuApiBoList.stream().collect(Collectors.toMap(NormalReceiptAndShelfDetailSkuApiBo::getSku, val -> val, (v1, v2) -> {
                List<ShelfQtyDetailApiBo> detailListAll = new ArrayList<>();
                detailListAll.addAll(v1.getDetailList());
                detailListAll.addAll(v2.getDetailList());
                v1.setDetailList(new ArrayList<>(detailListAll.stream().collect(Collectors.toMap(ShelfQtyDetailApiBo::getStockAttribute, value -> value, (o1, o2) -> {
                    o1.setReceiptQty(o1.getReceiptQty() + o2.getReceiptQty());
                    return o1;
                })).values()));
                return v1;
            })).values());
            //sku匹配产品id
            Map<String, Long> skuMatchId = productService.lambdaQuery()
                    .eq(ProductDo::getCompanyId, inputDo.getCompanyId())
                    .in(ProductDo::getSku, skuApiBoList.stream().map(NormalReceiptAndShelfDetailSkuApiBo::getSku).collect(Collectors.toList()))
                    .list().stream().collect(Collectors.toMap(ProductDo::getSku, ProductDo::getId));
            List<ShelfBySkuAddBo.SkuDetail> skuDetailList = new ArrayList<>();
            //组装上架数据
            for (NormalReceiptAndShelfDetailSkuApiBo skuApiBo : skuApiBoList) {
                Long productId = skuMatchId.get(skuApiBo.getSku());
                if (Objects.isNull(productId)) {
                    throw new BizException(SysConstant.Company_Not_Exist_Sku, "", skuApiBo.getSku());
                }
                ShelfBySkuAddBo.SkuDetail skuDetail = new ShelfBySkuAddBo.SkuDetail();
                skuDetail.setProductId(productId);
                skuDetail.setSku(skuApiBo.getSku());
                if (CollUtil.isEmpty(skuApiBo.getDetailList())) {
                    continue;
                }
                skuDetail.setShouldQty(skuApiBo.getDetailList().stream().map(ShelfQtyDetailApiBo::getReceiptQty).reduce(0, Integer::sum));
                List<ShelfQtyDetailBo> shelfQtyList = new ArrayList<>();
                for (ShelfQtyDetailApiBo detailApiBo : skuApiBo.getDetailList()) {
                    Long cellId = stockAttributeMatchCellId.get(detailApiBo.getStockAttribute());
                    if (Objects.isNull(cellId)) {
                        operateRecordService.operateAddRecordAndValue(
                                SysConstant.Rack_NotExist,
                                "",
                                inputDo.getId(),
                                userId,
                                username,
                                OperateTypeEnum.OrderInput
                        );
                        throw new BizException(SysConstant.Rack_NotExist);
                    }
                    shelfQtyList.add(ShelfQtyDetailBo.builder().cellId(cellId).qty(detailApiBo.getReceiptQty()).build());
                }
                skuDetail.setShelfQtyList(shelfQtyList);
                skuDetailList.add(skuDetail);
            }
            //上架
            ShelfBySkuAddBo shelfBySkuAddBo = new ShelfBySkuAddBo();
            shelfBySkuAddBo.setId(shelfDo.getId());
            shelfBySkuAddBo.setPasNo(shelfDo.getPasNo());
            shelfBySkuAddBo.setDetailList(skuDetailList);
            shelfBySkuAddBo.setJdApi(true);
            shelfService.shelfBySku(shelfBySkuAddBo, userId, username);
        }
    }

    @Override
    public boolean transferReceiptConfirm(TransferReceiptConfirmBo bo, Long userId, String username) {
        OrderInputDo orderInputDo = baseMapper.selectById(bo.getId());
        if (Objects.isNull(orderInputDo)) {
            throw new BizException(SysConstant.Inbound_Not_Exist);
        }
        //判断入库单状态 不为待入库和入库中直接返回
        if (!OrderInputStatusEnum.one.getValue().equals(orderInputDo.getStatus()) &&
            !OrderInputStatusEnum.two.getValue().equals(orderInputDo.getStatus())) {
            return true;
        }
        Long inputDoId = orderInputDo.getId();
        //修改入库单实收托盘数
        Integer palletQty = bo.getPalletQty();
        if (Objects.nonNull(palletQty) && palletQty > 0) {
            this.lambdaUpdate()
                    .eq(OrderInputDo::getId, inputDoId)
                    .set(OrderInputDo::getReceiptPalletsQty, palletQty + orderInputDo.getReceiptPalletsQty())
                    .update();
            //修改上架单实收托盘数日志
            this.updatePalletQtyLog(inputDoId, userId, username);
        }
        //生成上架单记录
        String shelfNo = this.addShelfRecord(
                orderInputDo,
                userId,
                username
        ).getPasNo();
        //查询待收货的箱子
        int waitReceiptBoxQty = boxService.lambdaQuery()
                .eq(OrderBoxDo::getOrderId, inputDoId)
                //避免已收货的箱子重复收货
                .isNull(OrderBoxDo::getReceivedTime)
                .count();
        List<TransferReceiptConfirmDetailBo> detailList = bo.getDetailList();
        if (CollUtil.isNotEmpty(detailList)) {
            Map<String, OrderBoxDo> boxTypeMatch = boxService.lambdaQuery()
                    .eq(OrderBoxDo::getOrderId, inputDoId)
                    .in(OrderBoxDo::getBoxTypeNumber, detailList.stream().map(TransferReceiptConfirmDetailBo::getBoxTypeNumber).filter(StrUtil::isNotBlank).collect(Collectors.toSet()))
                    .isNull(OrderBoxDo::getReceivedTime)
                    .list().stream().collect(Collectors.toMap(OrderBoxDo::getBoxTypeNumber, val -> val, (o1, o2) -> o1));
            boolean isAllReceipt = true;
            DateTime receivedTime = DateUtil.date();
            for (TransferReceiptConfirmDetailBo detailBo : detailList) {
                //箱类号
                String boxTypeNumber = detailBo.getBoxTypeNumber();
                //本次接收箱数
                Integer receiptBoxQty = detailBo.getReceiptBoxQty();
                //修改箱子内产品实收数量
                List<OrderBoxProductDo> bpList = boxProductService.lambdaQuery()
                        .eq(OrderBoxProductDo::getBoxTypeNumber, boxTypeNumber)
                        .list();
                bpList.forEach(bp -> bp.setReceiptQty(bp.getPerBoxQty()));
                boxProductService.updateBatchById(bpList);
                //查询该箱类号待收货的箱子
                OrderBoxDo boxDo = boxTypeMatch.get(boxTypeNumber);
                if (boxDo == null) {
                    continue;
                }
                if (receiptBoxQty >= boxDo.getBoxTotalQty()) {
                    //修改箱子实收产品数量及收货时间
                    boxService.lambdaUpdate()
                            .eq(OrderBoxDo::getId, boxDo.getId())
                            .set(OrderBoxDo::getReceivedTime, receivedTime)
                            .set(OrderBoxDo::getReceivedQty, boxDo.getSkuTotalQty() / boxDo.getBoxTotalQty() * receiptBoxQty)
                            .set(OrderBoxDo::getReceivedBoxQty, receiptBoxQty)
                            .set(OrderBoxDo::getPasNo, shelfNo)
                            .update();
                } else {
                    isAllReceipt = false;
                    //复制一条箱数据作部分上架用
                    OrderBoxDo receivedBox = BeanUtil.copyProperties(boxDo, OrderBoxDo.class);
                    receivedBox.setSkuTotalQty(boxDo.getSkuTotalQty() / boxDo.getBoxTotalQty() * receiptBoxQty);
                    receivedBox.setBoxTotalQty(receiptBoxQty);
                    receivedBox.setReceivedBoxQty(receiptBoxQty);
                    receivedBox.setReceivedQty(boxDo.getSkuTotalQty() / boxDo.getBoxTotalQty() * receiptBoxQty);
                    receivedBox.setReceivedTime(receivedTime);
                    receivedBox.setPasNo(shelfNo);
                    boxService.save(receivedBox);
                    boxService.lambdaUpdate()
                            .eq(OrderBoxDo::getId, boxDo.getId())
                            .set(OrderBoxDo::getReceivedBoxQty, 0)
                            .set(OrderBoxDo::getBoxTotalQty, boxDo.getBoxTotalQty() - receivedBox.getBoxTotalQty())
                            .set(OrderBoxDo::getReceivedQty, 0)
                            .set(OrderBoxDo::getSkuTotalQty, boxDo.getSkuTotalQty() - receivedBox.getSkuTotalQty())
                            .update();
                }
            }
            //修改入库单实收箱数及状态
            this.lambdaUpdate()
                    .eq(OrderInputDo::getId, inputDoId)
                    .set(
                            OrderInputDo::getReceiptBoxQty,
                            orderInputDo.getReceiptBoxQty()
                            + detailList.stream()
                                    .map(TransferReceiptConfirmDetailBo::getReceiptBoxQty)
                                    .reduce(0, Integer::sum)
                    )
                    .set(OrderInputDo::getStatus, OrderInputStatusEnum.two.getValue())
                    .update();
            //日志收货完成
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.OrderInput_Part_Receipt,
                    "",
                    inputDoId,
                    userId,
                    username,
                    OperateTypeEnum.OrderInput
            );
            if (waitReceiptBoxQty == detailList.size() && isAllReceipt) {
                //收货完成
                this.receiptFinish(inputDoId, userId, username, false);
                return true;
            }
        }
        return false;
    }

    /**
     * 创建上架单记录
     *
     * @return 上架单明细
     */
    private OrderInputShelfDo addShelfRecord(OrderInputDo inputDo, Long userId, String username) {
        //生成上架单号
        String shelfNo = this.generateShelfNo(inputDo.getInboundType());
        //新增上架数据
        OrderInputShelfDo shelfDo = OrderInputShelfDo.builder()
                .companyId(inputDo.getCompanyId())
                .whId(inputDo.getWarehouseId())
                .inboundType(inputDo.getInboundType())
                .inboundSourceNo(inputDo.getSourceNo())
                .pasNo(shelfNo)
                .status(OrderInputShelfStatusEnum.zero.getValue())
                .build();
        shelfService.save(shelfDo);
        operateRecordService.operateAddRecordAndValue(
                SysConstant.OrderInput_Create_Shelf,
                "",
                shelfDo.getId(),
                userId,
                username,
                OperateTypeEnum.Shelf
        );
        return shelfDo;
    }

    /**
     * 生成上架单号
     * 生成规则：PAS+入库类型数字+年月日+六位的序列号
     */
    private String generateShelfNo(Integer inboundType) {
        // 每周重置入库单序号
        //生成入库单号
        String shelfNoSeq = String.valueOf(redisTemplate.opsForValue().increment(RedisKeyConstants.SHELF_NO_SEQ));
        //序列号不足六位在前面补0
        if (shelfNoSeq.length() < 6) {
            shelfNoSeq = "000000".substring(0, 6 - shelfNoSeq.length()) + shelfNoSeq;
        }
        return "PAS"
               + inboundType
               + DateUtil.format(DateUtil.date(), "yyMMdd")
               + shelfNoSeq;
    }

    /**
     * 收货完成操作
     * 1.修改订单收货时间
     * 2.修改订单状态
     * 3.添加日志
     */
    private void receiptFinish(Long id, Long userId, String username, boolean isForceEnd) {
        //日志收货完成
        operateRecordService.operateAddRecordAndValue(
                isForceEnd ? SysConstant.OrderInput_Force_End : SysConstant.OrderInput_Receipt_Finish,
                "",
                id,
                userId,
                username,
                OperateTypeEnum.OrderInput,
                DateUtil.offset(DateUtil.date(), DateField.SECOND, 3)
        );
        progressService.lambdaUpdate()
                .eq(ProgressDo::getBizNumber, id)
                .eq(ProgressDo::getType, ProgressEnum.INBOUND.getTypeCode())
                .eq(ProgressDo::getBizType, "收货")
                .set(ProgressDo::getOperateBy, username)
                .set(ProgressDo::getOperateTime, DateUtil.date())
                .update();
        //收货时间/状态
        this.lambdaUpdate()
                .eq(OrderInputDo::getId, id)
                .set(OrderInputDo::getReceiptTime, DateUtil.date())
                .set(OrderInputDo::getStatus, OrderInputStatusEnum.three.getValue())
                .update();
    }

    @Override
    public void updatePalletQty(Long id, Integer palletQty, Long userId, String username) {
        OrderInputDo orderInputDo = baseMapper.selectById(id);
        if (!OrderInputStatusEnum.three.getValue().equals(orderInputDo.getStatus())) {
            throw new BizException(SysConstant.Status_Not_Modified_Pallet_Qty);
        }
        this.lambdaUpdate()
                .eq(OrderInputDo::getId, id)
                .set(OrderInputDo::getReceiptPalletsQty, palletQty)
                .update();
        this.updatePalletQtyLog(id, userId, username);
    }

    @Override
    public SingleResult getListPageStatus(OrderInputQueryBo bo) {
        Map<Integer, Integer> data = baseMapper.getListPageStatusSta(bo).stream().collect(Collectors.toMap(ListPageStatusStaDto::getStatus, ListPageStatusStaDto::getQty));
        List<Map<String, Object>> resultList = new ArrayList<>();
        for (OrderInputStatusEnum statusEnum : OrderInputStatusEnum.values()) {
            if (Objects.equals(statusEnum, OrderInputStatusEnum.zero) && !"oms".equals(bo.getSource())) {
                continue;
            }
            Map<String, Object> map = new HashMap<>(8);
            map.put("id", statusEnum.getValue());
            map.put("name", statusEnum.getDesc());
            map.put("sum", data.getOrDefault(statusEnum.getValue(), 0));
            resultList.add(map);
        }
        return SingleResult.success(resultList);
    }

    @Override
    public void excelExport(OrderInputQueryBo bo, Long tenantId, Long companyId, Long whId, HttpServletResponse response) throws IOException {
        // 设置下载的Excel名称，以当前时间为文件后缀
        String dateTime = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");
        String fileName = "InboundExport_" + dateTime + ".xlsx";
        // 设置响应输出的头类型
        response.setHeader("content-Type", "application/vnd.ms-excel");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        List<OrderInputListVo> list = baseMapper.getListPage(bo);
        //客户id&仓库id
        Set<Long> companyIdList = list.stream().map(OrderInputListVo::getCompanyId).collect(Collectors.toSet());
        Set<Long> whIdList = list.stream().map(OrderInputListVo::getWarehouseId).collect(Collectors.toSet());
        //客户&仓库
        Map<Long, String> companyIdMatch = companyService.lambdaQuery()
                .in(CollUtil.isNotEmpty(companyIdList), CompanyDo::getId, companyIdList)
                .list().stream()
                .collect(Collectors.toMap(CompanyDo::getId, val -> val.getName() + "(" + val.getCompanyCode() + ")"));
        Map<Long, String> whIdMatch = warehouseService.lambdaQuery()
                .in(CollUtil.isNotEmpty(whIdList), WarehouseDo::getId, whIdList)
                .list().stream()
                .collect(Collectors.toMap(WarehouseDo::getId, val -> val.getWhNameCn() + "(" + val.getWhCode() + ")"));
        OmsSystemEntity omsSet = null;
        WmsSystemEntity wmsSet = null;
        OmpSystemEntity ompSet = null;
        if ("oms".equals(bo.getSource())) {
            omsSet = systemSetService.getOmsSet(companyId);
        } else if ("wms".equals(bo.getSource())) {
            wmsSet = systemSetService.getWmsSet(whId);
        } else if ("omp".equals(bo.getSource())) {
            ompSet = systemSetService.getOmpSet(tenantId);
        }
        //入库单（按SKU）
        Map<String, Object> normalSkuExportList;
        //入库单（按箱唛）
        Map<String, Object> normalBoxExportList;
        //常规入库单
        List<OrderInputListVo> normalDtoList = list.stream()
                .filter(item -> InboundTypeEnum.one.getValue().equals(item.getInboundType()))
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(normalDtoList)) {
            //入库单id
            List<Long> idList = normalDtoList.stream().map(OrderInputListVo::getId).distinct().collect(Collectors.toList());
            //入库单id匹配箱子
            Map<Long, List<OrderBoxDo>> orderIdMatchBoxList = boxService.lambdaQuery()
                    .in(OrderBoxDo::getOrderId, idList)
                    .list().stream().collect(Collectors.toMap(OrderBoxDo::getBoxMarkNumber, val -> val, (o1, o2) -> {
                        o1.setReceivedQty(o1.getReceivedQty() + o2.getReceivedQty());
                        o1.setSkuTotalQty(o1.getSkuTotalQty() + o2.getSkuTotalQty());
                        return o1;
                    })).values().stream().collect(Collectors.groupingBy(OrderBoxDo::getOrderId));
            //箱唛号匹配箱子内产品信息
            Map<String, List<OrderBoxProductDo>> boxMarkNoMatchBpList = boxProductService.lambdaQuery()
                    .in(
                            OrderBoxProductDo::getBoxMarkNumber,
                            orderIdMatchBoxList.values().stream()
                                    .flatMap(Collection::stream)
                                    .map(OrderBoxDo::getBoxMarkNumber)
                                    .collect(Collectors.toSet())
                    )
                    .list().stream().collect(Collectors.groupingBy(OrderBoxProductDo::getBoxMarkNumber));
            //查询所有产品信息
            Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                    .in(ProductDo::getId, boxMarkNoMatchBpList.values().stream().flatMap(Collection::stream).map(OrderBoxProductDo::getProductId).collect(Collectors.toSet()))
                    .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
            //入库单（按SKU）
            normalSkuExportList = this.getNormalSkuExportList(
                    normalDtoList,
                    companyIdMatch,
                    whIdMatch,
                    orderIdMatchBoxList,
                    boxMarkNoMatchBpList,
                    productIdMatch,
                    wmsSet,
                    ompSet,
                    omsSet
            );
            //入库单（按箱唛）
            normalBoxExportList = this.getNormalBoxExportList(
                    normalDtoList,
                    companyIdMatch,
                    whIdMatch,
                    orderIdMatchBoxList,
                    boxMarkNoMatchBpList,
                    productIdMatch,
                    wmsSet,
                    ompSet,
                    omsSet
            );
        } else {
            //入库单（按SKU）
            normalSkuExportList = this.getNormalSkuExportList(null, null, null, null, null, null, null, null, null);
            //入库单（按箱唛）
            normalBoxExportList = this.getNormalBoxExportList(null, null, null, null, null, null, null, null, null);
        }

        //备货中转入库单
        Map<String, Object> transferExportList = this.getTransferExportList(
                list,
                companyIdMatch,
                whIdMatch,
                wmsSet,
                ompSet,
                omsSet
        );
        //添加表
        List<Map<String, Object>> sheetsList = new ArrayList<>(3);
        sheetsList.add(normalSkuExportList);
        sheetsList.add(normalBoxExportList);
        sheetsList.add(transferExportList);
        //创建excel文件的方法
        Workbook workbook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);
        //通过response输出流直接输入给客户端
        ServletOutputStream outputStream = response.getOutputStream();
        workbook.write(outputStream);
        outputStream.flush();
        outputStream.close();
    }

    @Override
    public void printReceivingNote(Long id, HttpServletResponse response) throws IOException {
        response.setHeader("content-Type", "application/pdf");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=printReceivingNote.pdf");
        OrderInputDo inputDo = this.getById(id);
        //入库单号
        String sourceNo = inputDo.getSourceNo();
        //入库单号条形
        byte[] sourceNoBarcode = BarCodeUtils.generateBarCode128(
                sourceNo,
                true,
                false,
                256,
                15,
                0.3f,
                0f,
                0f
        );
        ByteArrayInputStream sourceNoBarcodeImg = new ByteArrayInputStream(sourceNoBarcode);
        //跟踪号
        String trackingNumber = inputDo.getTrackingNumber();
        //到仓方式
        String enNameArrivalMode = ArrivalModeEnum.getEnName(inputDo.getArriveMethod());
        //获取客户信息
        CompanyDo companyDo = companyService.getById(inputDo.getCompanyId());
        String company = companyDo.getName() + "(" + companyDo.getCompanyCode() + ")";
        //通用参数
        Map<String, Object> data = new HashMap<>();
        data.put("sourceNo", sourceNo);
        data.put("trackingNumber", trackingNumber);
        data.put("arrivalMode", enNameArrivalMode);
        data.put("company", company);
        data.put("printTime", DateUtil.date());
        ConfigureBuilder configureBuilder = Configure.builder();
        XWPFTemplate template;
        ByteArrayOutputStream pdf;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ServletOutputStream out = response.getOutputStream();
        //入库类型
        Integer inboundType = inputDo.getInboundType();
        switch (inboundType) {
            case 1:
                //常规入库
                List<OrderBoxDo> boxList = boxService.lambdaQuery()
                        .eq(OrderBoxDo::getOrderId, id)
                        .list();
                //合并箱
                boxList = new ArrayList<>(boxList.stream().collect(Collectors.toMap(OrderBoxDo::getBoxMarkNumber, v -> v, (v1, v2) -> {
                    v1.setReceivedQty(v1.getReceivedQty() + v2.getReceivedQty());
                    v1.setSkuTotalQty(v1.getSkuTotalQty() + v2.getSkuTotalQty());
                    return v1;
                })).values());
                //根据箱唛分组
                Map<String, List<OrderBoxProductDo>> boxMarkNumberMatch = boxProductService.lambdaQuery()
                        .in(OrderBoxProductDo::getBoxMarkNumber, boxList.stream().map(OrderBoxDo::getBoxMarkNumber).collect(Collectors.toList()))
                        .list().stream().collect(Collectors.groupingBy(OrderBoxProductDo::getBoxMarkNumber));
                data.put("total", boxList.size());
                data.put("sourceNoBarcode", Pictures.ofStream(sourceNoBarcodeImg, PictureType.PNG).size(250, 70).create());
                data.put("skuKind", boxMarkNumberMatch.values().stream().flatMap(Collection::stream).map(OrderBoxProductDo::getSku).distinct().count());
                data.put("skuTotalQty", boxMarkNumberMatch.values().stream().flatMap(Collection::stream).map(OrderBoxProductDo::getPerBoxQty).reduce(0, Integer::sum));
                List<MultiLevelLoopRowTableDto> normalList = new ArrayList<>();
                for (int i = 1; i <= boxList.size(); i++) {
                    OrderBoxDo box = boxList.get(i - 1);
                    MultiLevelLoopRowTableDto tableDto = new MultiLevelLoopRowTableDto();
                    tableDto.setNo(String.valueOf(i));
                    tableDto.setType(box.getBoxMarkNumber());
                    tableDto.setQty(String.valueOf(1));
                    List<OrderBoxProductDo> boxProductDoList = boxMarkNumberMatch.get(box.getBoxMarkNumber());
                    for (OrderBoxProductDo boxProductDo : boxProductDoList) {
                        HashMap<String, String> map = new HashMap<>();
                        map.put("sku", boxProductDo.getSku());
                        map.put("skuQty", String.valueOf(boxProductDo.getPerBoxQty()));
                        map.put("receivedSkuQty", String.valueOf(boxProductDo.getReceiptQty()));
                        tableDto.getSonList().add(map);
                    }
                    normalList.add(tableDto);
                }
                data.put("table",
                        this.getReceivingNoteTableData(
                                normalList,
                                new DynamicTableDto(0, 0, 3, 6, 1)
                        )
                );
                //模板配置
                configureBuilder.bind("table", new DynamicTablePolicy());
                template = XWPFTemplate.compile(this.getClass().getClassLoader().getResourceAsStream("wordTemplates/inReceivingNote-normal.docx"), configureBuilder.build()).render(data);
                template.writeAndClose(bos);
                //转换为pdf
                pdf = AsposePdfUtils.word2pdf(new ByteArrayInputStream(bos.toByteArray()));
                out.write(pdf.toByteArray());
                bos.flush();
                out.flush();
                PoitlIOUtils.closeQuietlyMulti(template, bos, out, pdf);
                break;
            case 2:
                //备货中转
                //构建数据
                List<OrderBoxDo> boxDoList = boxService.lambdaQuery()
                        .eq(OrderBoxDo::getOrderId, id)
                        .list().stream().collect(Collectors.toMap(OrderBoxDo::getBoxTypeNumber, val -> val, (o1, o2) -> {
                            o1.setReceivedBoxQty(o1.getReceivedBoxQty() + o2.getReceivedBoxQty());
                            o1.setBoxTotalQty(o1.getBoxTotalQty() + o2.getBoxTotalQty());
                            return o1;
                        })).values().stream().sorted(Comparator.comparing(OrderBoxDo::getBoxTypeNumber)).collect(Collectors.toList());
                data.put("sourceNoBarcode", Pictures.ofStream(sourceNoBarcodeImg, PictureType.PNG).size(230, 60).create());
                data.put("total", boxDoList.stream().map(OrderBoxDo::getBoxTotalQty).reduce(0, Integer::sum));
                data.put("boxTypeKind", boxDoList.size());
                List<Map<String, Object>> transferList = new ArrayList<>(boxDoList.size());
                for (int i = 1; i <= boxDoList.size(); i++) {
                    OrderBoxDo boxDo = boxDoList.get(i - 1);
                    HashMap<String, Object> map = new HashMap<>();
                    map.put("no", i);
                    map.put("boxTypeNumber", boxDo.getBoxTypeNumber());
                    map.put("customizeBarcode", boxDo.getCustomizeBarcode());
                    map.put("receivedBoxQty", boxDo.getReceivedBoxQty());
                    map.put("boxTotalQty", boxDo.getBoxTotalQty());
                    transferList.add(map);
                }
                data.put("list", transferList);
                //模板配置
                //使用行循环策略
                configureBuilder.bind("list", new LoopRowTableRenderPolicy());
                template = XWPFTemplate.compile(this.getClass().getClassLoader().getResourceAsStream("wordTemplates/inReceivingNote-transfer.docx"), configureBuilder.build()).render(data);
                template.writeAndClose(bos);
                //转换为pdf
                pdf = AsposePdfUtils.word2pdf(new ByteArrayInputStream(bos.toByteArray()));
                out.write(pdf.toByteArray());
                bos.flush();
                out.flush();
                PoitlIOUtils.closeQuietlyMulti(template, bos, out, pdf);
                break;
            default:
        }
    }

    @Override
    public void printReceivingNoteBoxBarcode(Long id, HttpServletResponse response) throws IOException {
        response.setHeader("Content-Type", "application/pdf");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=printReceivingNoteBoxBarcode.pdf");
        OrderInputDo inputDo = this.getById(id);
        //入库单号
        String sourceNo = inputDo.getSourceNo();
        //入库单号条形
        byte[] sourceNoBarcode = BarCodeUtils.generateBarCode128(
                sourceNo,
                true,
                false,
                256,
                15,
                0.3f,
                0f,
                0f
        );
        ByteArrayInputStream sourceNoBarcodeImg = new ByteArrayInputStream(sourceNoBarcode);
        //跟踪号
        String trackingNumber = inputDo.getTrackingNumber();
        //到仓方式
        String enNameArrivalMode = ArrivalModeEnum.getEnName(inputDo.getArriveMethod());
        //获取客户信息
        CompanyDo companyDo = companyService.getById(inputDo.getCompanyId());
        String company = companyDo.getName() + "(" + companyDo.getCompanyCode() + ")";
        //通用参数
        Map<String, Object> data = new HashMap<>();
        data.put("sourceNo", sourceNo);
        data.put("trackingNumber", trackingNumber);
        data.put("arrivalMode", enNameArrivalMode);
        data.put("company", company);
        data.put("sourceNoBarcode", Pictures.ofStream(sourceNoBarcodeImg, PictureType.PNG).size(250, 70).create());
        data.put("printTime", DateUtil.date());
        Resource resource;
        File templateFile;
        ConfigureBuilder configureBuilder = Configure.builder();
        XWPFTemplate template;
        ByteArrayOutputStream pdf;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ServletOutputStream out = response.getOutputStream();
        //入库类型
        Integer inboundType = inputDo.getInboundType();
        switch (inboundType) {
            case 1:
                //常规入库
                List<OrderBoxDo> boxList = boxService.lambdaQuery()
                        .eq(OrderBoxDo::getOrderId, id)
                        .list();
                //合并箱
                boxList = new ArrayList<>(boxList.stream().collect(Collectors.toMap(OrderBoxDo::getBoxMarkNumber, v -> v, (v1, v2) -> {
                    v1.setReceivedQty(v1.getReceivedQty() + v2.getReceivedQty());
                    v1.setSkuTotalQty(v1.getSkuTotalQty() + v2.getSkuTotalQty());
                    return v1;
                })).values());
                //根据箱唛分组
                Map<String, List<OrderBoxProductDo>> boxMarkNumberMatch = boxProductService.lambdaQuery()
                        .in(OrderBoxProductDo::getBoxMarkNumber, boxList.stream().map(OrderBoxDo::getBoxMarkNumber).collect(Collectors.toList()))
                        .list().stream().collect(Collectors.groupingBy(OrderBoxProductDo::getBoxMarkNumber));
                data.put("total", boxList.size());
                data.put("skuKind", boxMarkNumberMatch.values().stream().flatMap(Collection::stream).map(OrderBoxProductDo::getSku).distinct().count());
                data.put("skuTotalQty", boxMarkNumberMatch.values().stream().flatMap(Collection::stream).map(OrderBoxProductDo::getPerBoxQty).reduce(0, Integer::sum));
                List<MultiLevelLoopRowTableDto> normalList = new ArrayList<>();
                for (int i = 1; i <= boxList.size(); i++) {
                    OrderBoxDo box = boxList.get(i - 1);
                    MultiLevelLoopRowTableDto tableDto = new MultiLevelLoopRowTableDto();
                    tableDto.setNo(String.valueOf(i));
                    tableDto.setType(box.getBoxMarkNumber());
                    tableDto.setQty(String.valueOf(1));
                    List<OrderBoxProductDo> boxProductDoList = boxMarkNumberMatch.get(box.getBoxMarkNumber());
                    for (OrderBoxProductDo boxProductDo : boxProductDoList) {
                        HashMap<String, String> map = new HashMap<>();
                        map.put("sku", boxProductDo.getSku());
                        map.put("skuQty", String.valueOf(boxProductDo.getPerBoxQty()));
                        map.put("receivedSkuQty", String.valueOf(boxProductDo.getReceiptQty()));
                        tableDto.getSonList().add(map);
                    }
                    normalList.add(tableDto);
                }
                data.put("table",
                        this.getReceivingNoteBoxBarcodeTableData(
                                normalList,
                                new DynamicTableDto(0, 0, 3, 6, 1)
                        )
                );
                //模板配置
                configureBuilder.bind("table", new DynamicTablePolicy());
                template = XWPFTemplate.compile(this.getClass().getClassLoader().getResourceAsStream("wordTemplates/inReceivingNote-normal.docx"), configureBuilder.build()).render(data);
                template.writeAndClose(bos);
                //转换为pdf
                pdf = AsposePdfUtils.word2pdf(new ByteArrayInputStream(bos.toByteArray()));
                out.write(pdf.toByteArray());
                bos.flush();
                out.flush();
                PoitlIOUtils.closeQuietlyMulti(template, bos, out, pdf);
                break;
            case 2:
                //备货中转
                //构建数据
                List<OrderBoxDo> boxDoList = boxService.lambdaQuery()
                        .eq(OrderBoxDo::getOrderId, id)
                        .list().stream().collect(Collectors.toMap(OrderBoxDo::getBoxTypeNumber, val -> val, (o1, o2) -> {
                            o1.setReceivedBoxQty(o1.getReceivedBoxQty() + o2.getReceivedBoxQty());
                            o1.setBoxTotalQty(o1.getBoxTotalQty() + o2.getBoxTotalQty());
                            return o1;
                        })).values().stream().sorted(Comparator.comparing(OrderBoxDo::getBoxTypeNumber)).collect(Collectors.toList());
                data.put("total", boxDoList.stream().map(OrderBoxDo::getBoxTotalQty).reduce(0, Integer::sum));
                data.put("boxTypeKind", boxDoList.size());
                List<Map<String, Object>> transferList = new ArrayList<>(boxDoList.size());
                for (int i = 1; i <= boxDoList.size(); i++) {
                    OrderBoxDo boxDo = boxDoList.get(i - 1);
                    HashMap<String, Object> map = new HashMap<>();
                    map.put("no", i);
                    byte[] boxBarcode = BarCodeUtils.generateBarCode128(
                            boxDo.getBoxTypeNumber(),
                            false,
                            false,
                            256,
                            15,
                            0.4f,
                            0,
                            0
                    );
                    ByteArrayInputStream boxBarcodeImg = new ByteArrayInputStream(boxBarcode);
                    map.put("boxTypeNumber", Pictures.ofStream(boxBarcodeImg, PictureType.PNG).size(200, 55).create());
                    map.put("customizeBarcode", boxDo.getCustomizeBarcode());
                    map.put("receivedBoxQty", boxDo.getReceivedBoxQty());
                    map.put("boxTotalQty", boxDo.getBoxTotalQty());
                    transferList.add(map);
                }
                data.put("list", transferList);
                //模板配置
                //使用行循环策略
                configureBuilder.bind("list", new LoopRowTableRenderPolicy());
                template = XWPFTemplate.compile(this.getClass().getClassLoader().getResourceAsStream("wordTemplates/inReceivingNote-transfer.docx"), configureBuilder.build()).render(data);
                template.writeAndClose(bos);
                //转换为pdf
                pdf = AsposePdfUtils.word2pdf(new ByteArrayInputStream(bos.toByteArray()));
                out.write(pdf.toByteArray());
                bos.flush();
                out.flush();
                PoitlIOUtils.closeQuietlyMulti(template, bos, out, pdf);
                break;
            default:
        }
    }

    @Override
    public void printReceivingNoteSku(Long id, Long whId, HttpServletResponse response) throws IOException {
        response.setHeader("Content-Type", "application/pdf");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=printReceivingNoteSku.pdf");
        OrderInputDo inputDo = this.getById(id);
        //入库单号
        String sourceNo = inputDo.getSourceNo();
        //入库单号条形
        byte[] sourceNoBarcode = BarCodeUtils.generateBarCode128(
                sourceNo,
                true,
                false,
                256,
                15,
                0.3f,
                0f,
                0f
        );
        ByteArrayInputStream sourceNoBarcodeImg = new ByteArrayInputStream(sourceNoBarcode);
        //跟踪号
        String trackingNumber = inputDo.getTrackingNumber();
        //到仓方式
        String enNameArrivalMode = ArrivalModeEnum.getEnName(inputDo.getArriveMethod());
        //获取客户信息
        CompanyDo companyDo = companyService.getById(inputDo.getCompanyId());
        String company = companyDo.getName() + "(" + companyDo.getCompanyCode() + ")";
        //通用参数
        Map<String, Object> data = new HashMap<>();
        data.put("sourceNo", sourceNo);
        data.put("trackingNumber", trackingNumber);
        data.put("arrivalMode", enNameArrivalMode);
        data.put("company", company);
        data.put("sourceNoBarcode", Pictures.ofStream(sourceNoBarcodeImg, PictureType.PNG).size(245, 70).create());
        data.put("printTime", DateUtil.date());
        Resource resource;
        File templateFile;
        ConfigureBuilder configureBuilder = Configure.builder();
        XWPFTemplate template;
        ByteArrayOutputStream pdf;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ServletOutputStream out = response.getOutputStream();
        //入库类型
        Integer inboundType = inputDo.getInboundType();
        switch (inboundType) {
            case 1:
                //常规入库
                List<OrderBoxDo> boxList = boxService.lambdaQuery()
                        .eq(OrderBoxDo::getOrderId, id)
                        .list();
                //根据产品id分组
                Map<Long, OrderBoxProductDo> productIdMap = boxProductService.lambdaQuery()
                        .in(OrderBoxProductDo::getBoxMarkNumber, boxList.stream().map(OrderBoxDo::getBoxMarkNumber).collect(Collectors.toList()))
                        .list().stream().collect(Collectors.toMap(OrderBoxProductDo::getProductId, val -> val, (o1, o2) -> {
                            o1.setReceiptQty(o1.getReceiptQty() + o2.getReceiptQty());
                            o1.setPerBoxQty(o1.getPerBoxQty() + o2.getPerBoxQty());
                            return o1;
                        }));
                Map<Long, ProductDo> productIdMatchDo = productService.lambdaQuery()
                        .in(ProductDo::getId, productIdMap.keySet())
                        .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
                data.put("skuKind", productIdMap.keySet().size());
                data.put("skuTotalQty", productIdMap.values().stream().map(OrderBoxProductDo::getPerBoxQty).reduce(0, Integer::sum));
                WmsSystemEntity wmsSet = systemSetService.getWmsSet(whId);
                String unit = wmsSet.getUnitMeasurement().getUnit();
                if (Objects.isNull(unit) || "cm".equals(unit)) {
                    data.put("unit", "Metric（公制）");
                } else if ("in".equals(unit)) {
                    data.put("unit", "British（英制）");
                }
                List<Map<String, Object>> skuList = new ArrayList<>();
                AtomicInteger i = new AtomicInteger(1);
                for (Map.Entry<Long, OrderBoxProductDo> bpEntry : productIdMap.entrySet()) {
                    HashMap<String, Object> map = new HashMap<>();
                    ProductDo productDo = productIdMatchDo.get(bpEntry.getKey());
                    map.put("no", i.getAndIncrement());
                    map.put("sku", productDo.getSku());
                    map.put("qty", bpEntry.getValue().getPerBoxQty());
                    String dimension = "";
                    BigDecimal weight = BigDecimal.ZERO;
                    if (Objects.isNull(unit) || "cm".equals(unit)) {
                        dimension = (productDo.getWmsLength() != null ? productDo.getWmsLength() : productDo.getLength())
                                    + "*" + (productDo.getWmsWidth() != null ? productDo.getWmsWidth() : productDo.getWidth())
                                    + "*" + (productDo.getWmsHeight() != null ? productDo.getWmsHeight() : productDo.getHeight());
                        weight = productDo.getWmsWeight() != null ? productDo.getWmsWeight() : productDo.getWeight();
                    } else if ("in".equals(unit)) {
                        dimension = (productDo.getWmsLength() != null ? productDo.getWmsLength() : productDo.getLength())
                                    + "*" + (productDo.getWmsWidth() != null ? productDo.getWmsWidth() : productDo.getWidth())
                                    + "*" + (productDo.getWmsHeight() != null ? productDo.getWmsHeight() : productDo.getHeight());
                        weight = productDo.getWmsWeight() != null ? productDo.getWmsWeight() : productDo.getWeight();
                    }
                    map.put("dimension", dimension);
                    map.put("weight", weight);
                    map.put("received", bpEntry.getValue().getReceiptQty());
                    skuList.add(map);
                }
                data.put("list", skuList);
                //模板配置
                //使用行循环策略
                configureBuilder.bind("list", new LoopRowTableRenderPolicy());
                template = XWPFTemplate.compile(this.getClass().getClassLoader().getResourceAsStream("wordTemplates/inReceivingNote-sku.docx"), configureBuilder.build()).render(data);
                template.writeAndClose(bos);
                //转换为pdf
                pdf = AsposePdfUtils.word2pdf(new ByteArrayInputStream(bos.toByteArray()));
                out.write(pdf.toByteArray());
                bos.flush();
                out.flush();
                PoitlIOUtils.closeQuietlyMulti(template, bos, out, pdf);
                break;
            case 2:
                break;
            default:
        }
    }

    @Override
    public void submitBatch(InboundSubmitBo bo, Long userId, String username) {
        for (Long id : bo.getInboundIdList()) {
            this.submit(id, userId, username);
        }
    }

    @Override
    public CommonBatchOperationVo cancel(InboundCancelBo bo, Long userId, String username) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        res.setTotal(bo.getInboundIdList().size());
        Map<Long, OrderInputDo> inputDoMap = this.listByIds(bo.getInboundIdList()).stream().collect(Collectors.toMap(OrderInputDo::getId, orderInboundDo -> orderInboundDo));
        for (Long id : bo.getInboundIdList()) {
            OrderInputDo inputDo = inputDoMap.get(id);
            if (Objects.isNull(inputDo)) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", id);
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Inbound_Not_Exist));
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (inputDo.getStatus() > OrderInputStatusEnum.one.getValue()) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", inputDo.getSourceNo());
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Inbound_Can_Not_Cancel));
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.OrderInput_Cancel,
                    "",
                    id,
                    userId,
                    username,
                    OperateTypeEnum.OrderInput
            );
            progressService.remove(
                    Wrappers.<ProgressDo>lambdaQuery()
                            .eq(ProgressDo::getBizNumber, id)
                            .eq(ProgressDo::getType, ProgressEnum.INBOUND.getTypeCode())
                            .ne(ProgressDo::getBizType, "创建")
            );
            progressService.save(
                    ProgressDo.builder()
                            .bizNumber(id)
                            .type(ProgressEnum.INBOUND.getTypeCode())
                            .bizType("取消")
                            .operateBy(username)
                            .operateTime(DateUtil.date())
                            .sort(2)
                            .build());
            this.lambdaUpdate()
                    .eq(OrderInputDo::getId, id)
                    .set(OrderInputDo::getStatus, OrderInputStatusEnum.five.getValue())
                    .set(OrderInputDo::getCanceledTime, DateUtil.date())
                    .update();
            res.setSuccess(res.getSuccess() + 1);
        }
        return res;
    }

    @Override
    public void getNormalInboundImportTemplate(HttpServletResponse response, Long tenantId, Long userId, Long companyId, String source) throws IOException {
        TemplateExportParams params = new TemplateExportParams(
                "excelTemplates/NormalInboundImport.xlsx", true);
        //每个sheet的数据对应一个map
        Map<String, Object> sheetMap = new HashMap<>(4);
        //处理第一个sheet(Warehouse仓库信息)
        List<WarehouseDo> warehouseDoList;
        if (Objects.nonNull(companyId)) {
            warehouseDoList = warehouseService.getWarehouseCompanyDo(companyId, tenantId);
        } else {
            warehouseDoList = warehouseService.lambdaQuery()
                    .eq(WarehouseDo::getTenantId, tenantId)
                    .eq(WarehouseDo::getStatus, 0)
                    .list();
        }
        List<Map<String, String>> whList = new ArrayList<>();
        for (WarehouseDo warehouseDo : warehouseDoList) {
            Map<String, String> whMap = new HashMap<>();
            whMap.put("warehouseCode", warehouseDo.getWhCode());
            whMap.put("warehouseName", warehouseDo.getWhNameCn());
            whList.add(whMap);
        }
        sheetMap.put("whList", whList);
        //处理第二个sheet(Customer客户信息)
        List<CompanyRes> companyResList = companyService.getCompanyList(userId, tenantId, source);
        List<Map<String, String>> companyList = new ArrayList<>();
        for (CompanyRes companyRes : companyResList) {
            Map<String, String> customerMap = new HashMap<>();
            customerMap.put("companyCode", companyRes.getCompanyCode());
            customerMap.put("companyName", companyRes.getCompanyName());
            companyList.add(customerMap);
        }
        sheetMap.put("companyList", companyList);
        response.setCharacterEncoding("UTF-8");
        response.setHeader("content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=NormalInboundImport.xlsx");
        //导出数据
        try (Workbook book = ExcelExportUtil.exportExcel(params, sheetMap)) {
            book.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
    }

    @Override
    public void getTransferInboundImportTemplate(HttpServletResponse response, Long tenantId, Long userId, Long companyId, String source) throws IOException {
        TemplateExportParams params = new TemplateExportParams(
                "excelTemplates/TransferInboundImport.xlsx", true);
        //每个sheet的数据对应一个map
        Map<String, Object> sheetMap = new HashMap<>(4);
        //处理第一个sheet(Warehouse仓库信息)
        List<WarehouseDo> warehouseDoList;
        if (Objects.nonNull(companyId)) {
            warehouseDoList = warehouseService.getWarehouseCompanyDo(companyId, tenantId);
        } else {
            warehouseDoList = warehouseService.lambdaQuery()
                    .eq(WarehouseDo::getTenantId, tenantId)
                    .eq(WarehouseDo::getStatus, 0)
                    .list();
        }
        List<Map<String, String>> whList = new ArrayList<>();
        for (WarehouseDo warehouseDo : warehouseDoList) {
            Map<String, String> whMap = new HashMap<>(4);
            whMap.put("warehouseCode", warehouseDo.getWhCode());
            whMap.put("warehouseName", warehouseDo.getWhNameCn());
            whList.add(whMap);
        }
        sheetMap.put("whList", whList);
        //处理第二个sheet(Customer客户信息)
        List<CompanyRes> companyResList = companyService.getCompanyList(userId, tenantId, source);
        List<Map<String, String>> companyList = new ArrayList<>();
        for (CompanyRes companyRes : companyResList) {
            Map<String, String> companyMap = new HashMap<>(4);
            companyMap.put("companyCode", companyRes.getCompanyCode());
            companyMap.put("companyName", companyRes.getCompanyName());
            companyList.add(companyMap);
        }
        sheetMap.put("companyList", companyList);
        List<Map<String, String>> boxList = new ArrayList<>();
        InputBoxTypeQueryBo boxTypeQueryBo = new InputBoxTypeQueryBo();
        boxTypeQueryBo.setPageSize(1000);
        Collection<InputBoxTypeNumberVo> boxTypeNumberVoList = boxService.getBoxTypeNumberListPage(boxTypeQueryBo).getData();
        for (InputBoxTypeNumberVo boxTypeNumberVo : boxTypeNumberVoList) {
            Map<String, String> boxTypeMap = new HashMap<>(4);
            boxTypeMap.put("boxNo", boxTypeNumberVo.getBoxTypeNumber());
            boxTypeMap.put("skuInfo", boxTypeNumberVo.getProductList().stream().map(item -> item.getSku() + "*" + item.getPerBoxQty()).collect(Collectors.joining(",")));
            boxList.add(boxTypeMap);
        }
        sheetMap.put("boxList", boxList);
        response.setCharacterEncoding("UTF-8");
        response.setHeader("content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=TransferInboundImport.xlsx");
        //导出数据
        try (Workbook book = ExcelExportUtil.exportExcel(params, sheetMap)) {
            book.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
    }

    @Override
    public CommonBatchOperationVo importNormalInbound(MultipartFile file, Long tenantId, Long userId, String username) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<NormalInboundImportDto> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, NormalInboundImportDto.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        List<String> exceptionTrackingNumberList = importDtoList.stream().filter(
                i -> StrUtil.isBlank(i.getTrackingNumber())
                     || StrUtil.isBlank(i.getWarehouseCode())
                     || StrUtil.isBlank(i.getCompanyCode())
                     || StrUtil.isBlank(i.getArrivalMethod())
                     || StrUtil.isBlank(i.getBoxIdentificationNo())
                     || Objects.isNull(i.getBoxTotalQty())
                     || StrUtil.isBlank(i.getSku())
                     || Objects.isNull(i.getPerBoxQty())
        ).map(NormalInboundImportDto::getTrackingNumber).collect(Collectors.toList());
        importDtoList = importDtoList.stream().filter(i -> !exceptionTrackingNumberList.contains(i.getTrackingNumber())).collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", "Exception");
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        Map<String, WarehouseDo> whCodeMatch = warehouseService.lambdaQuery()
                .eq(WarehouseDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(WarehouseDo::getWhCode, val -> val));
        Map<String, CompanyDo> companyCodeMatch = companyService.lambdaQuery()
                .eq(CompanyDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(CompanyDo::getCompanyCode, val -> val));
        Map<String, ProductDo> companyIdSkuMatch = productService.lambdaQuery()
                .eq(ProductDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(key -> key.getCompanyId() + key.getSku(), val -> val));
        //根据trackingNumber相同组成为一个订单
        Map<String, List<NormalInboundImportDto>> dtoListByTrackNoGroup = importDtoList.stream().collect(Collectors.groupingBy(NormalInboundImportDto::getTrackingNumber));
        for (Map.Entry<String, List<NormalInboundImportDto>> entry : dtoListByTrackNoGroup.entrySet()) {
            List<NormalInboundImportDto> dtoListByTrackNo = entry.getValue();
            //校验数据
            NormalInboundImportDto dtoByTrackNo = dtoListByTrackNo.get(0);
            if (whCodeMatch.get(dtoByTrackNo.getWarehouseCode()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dtoByTrackNo.getTrackingNumber());
                map.put("errorMsg", "Warehouse Code/仓库代码[" + dtoByTrackNo.getWarehouseCode() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + dtoListByTrackNo.size());
                res.getFailedList().add(map);
                continue;
            }
            if (companyCodeMatch.get(dtoByTrackNo.getCompanyCode()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dtoByTrackNo.getTrackingNumber());
                map.put("errorMsg", "客户编码/Customer Code[" + dtoByTrackNo.getCompanyCode() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + dtoListByTrackNo.size());
                res.getFailedList().add(map);
                continue;
            }
            if (ArrivalModeEnum.getCode(dtoByTrackNo.getArrivalMethod()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dtoByTrackNo.getTrackingNumber());
                map.put("errorMsg", "到货方式/Arrival Mode[" + dtoByTrackNo.getArrivalMethod() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + dtoListByTrackNo.size());
                res.getFailedList().add(map);
                continue;
            }
            if (StrUtil.isNotBlank(dtoByTrackNo.getUnit()) && !"公制(cm/kg)".equals(dtoByTrackNo.getUnit()) && !"英制(inch/lb)".equals(dtoByTrackNo.getUnit())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dtoByTrackNo.getTrackingNumber());
                map.put("errorMsg", "Unit/尺寸单位 Wrong,Please check!");
                res.setFailed(res.getFailed() + dtoListByTrackNo.size());
                res.getFailedList().add(map);
                continue;
            }
            Optional<NormalInboundImportDto> skuCannotFind = dtoListByTrackNo.stream().filter(i -> companyIdSkuMatch.get(companyCodeMatch.get(dtoByTrackNo.getCompanyCode()).getId() + i.getSku()) == null).findFirst();
            if (skuCannotFind.isPresent()) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dtoByTrackNo.getTrackingNumber());
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Company_Not_Exist_Sku, skuCannotFind.get().getCompanyCode(), skuCannotFind.get().getSku()));
                res.setFailed(res.getFailed() + dtoListByTrackNo.size());
                res.getFailedList().add(map);
                continue;
            }
//            OrderInputDo inputDo = new OrderInputDo();
//            BeanUtils.copyProperties(dtoByTrackNo, inputDo);
            OrderInputDo inputDo = BeanConvert.INSTANCE.normalInboundImportDtoToInputDo(dtoByTrackNo);
            //生成入库单号
            String orderInputNo = this.generateOrderInputNo(InboundTypeEnum.one.getValue());
            inputDo.setSourceNo(orderInputNo);
            inputDo.setWarehouseId(whCodeMatch.get(dtoByTrackNo.getWarehouseCode()).getId());
            inputDo.setCompanyId(companyCodeMatch.get(dtoByTrackNo.getCompanyCode()).getId());
            inputDo.setArriveMethod(ArrivalModeEnum.getCode(dtoByTrackNo.getArrivalMethod()));
            inputDo.setInboundType(InboundTypeEnum.one.getValue());
            inputDo.setSkuTotalQty(dtoListByTrackNo.stream().map(item -> item.getBoxTotalQty() * item.getPerBoxQty()).reduce(0, Integer::sum));
            inputDo.setBoxTotalQty(dtoByTrackNo.getBoxTotalQty());
            inputDo.setStatus(OrderInputStatusEnum.zero.getValue());
            this.save(inputDo);
            createProgress(inputDo, username);
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Inbound_Order_Import,
                    "",
                    inputDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.OrderInput
            );
            //根据箱子种类分组
            Map<String, List<NormalInboundImportDto>> dtoListBySeqGroup = dtoListByTrackNo.stream().collect(Collectors.groupingBy(NormalInboundImportDto::getBoxIdentificationNo));
            AtomicInteger i = new AtomicInteger(1);
            for (Map.Entry<String, List<NormalInboundImportDto>> seqEntry : dtoListBySeqGroup.entrySet()) {
                List<NormalInboundImportDto> dtoListBySeq = seqEntry.getValue();
                NormalInboundImportDto dtoBySeq = dtoListBySeq.get(0);
                OrderBoxDo boxDo = new OrderBoxDo();
                boxDo.setOrderId(inputDo.getId());
                boxDo.setCustomizeBarcode(dtoBySeq.getCustomizeBarcode());
                if (StrUtil.isNotBlank(dtoByTrackNo.getUnit()) && "公制(cm/kg)".equals(dtoByTrackNo.getUnit())) {
                    if (Objects.nonNull(dtoBySeq.getPackageLength())) {
                        boxDo.setLength(dtoBySeq.getPackageLength());
                        boxDo.setLengthBs(dtoBySeq.getPackageLength().divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                    }
                    if (Objects.nonNull(dtoBySeq.getPackageWidth())) {
                        boxDo.setWidth(dtoBySeq.getPackageWidth());
                        boxDo.setWidthBs(dtoBySeq.getPackageWidth().divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                    }
                    if (Objects.nonNull(dtoBySeq.getPackageHeight())) {
                        boxDo.setHeight(dtoBySeq.getPackageHeight());
                        boxDo.setHeightBs(dtoBySeq.getPackageHeight().divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                    }
                    if (Objects.nonNull(dtoBySeq.getPackageWeight())) {
                        boxDo.setWeight(dtoBySeq.getPackageWeight());
                        boxDo.setWeightBs(dtoBySeq.getPackageWeight().multiply(BigDecimal.valueOf(2.2046)).setScale(3, RoundingMode.HALF_UP));
                    }
                } else if (StrUtil.isNotBlank(dtoByTrackNo.getUnit()) && "英制(inch/lb)".equals(dtoByTrackNo.getUnit())) {
                    //英制
                    if (Objects.nonNull(dtoBySeq.getPackageLength())) {
                        boxDo.setLength(dtoBySeq.getPackageLength().multiply(BigDecimal.valueOf(2.54)).setScale(3, RoundingMode.HALF_UP));
                        boxDo.setLengthBs(dtoBySeq.getPackageLength());
                    }
                    if (Objects.nonNull(dtoBySeq.getPackageWidth())) {
                        boxDo.setWidth(dtoBySeq.getPackageWidth().multiply(BigDecimal.valueOf(2.54)).setScale(3, RoundingMode.HALF_UP));
                        boxDo.setWidthBs(dtoBySeq.getPackageWidth());
                    }
                    if (Objects.nonNull(dtoBySeq.getPackageHeight())) {
                        boxDo.setHeight(dtoBySeq.getPackageHeight().multiply(BigDecimal.valueOf(2.54)).setScale(3, RoundingMode.HALF_UP));
                        boxDo.setHeightBs(dtoBySeq.getPackageHeight());
                    }
                    if (Objects.nonNull(dtoBySeq.getPackageWeight())) {
                        boxDo.setWeight(dtoBySeq.getPackageWeight().divide(BigDecimal.valueOf(2.2046), 3, RoundingMode.HALF_UP));
                        boxDo.setWeightBs(dtoBySeq.getPackageWeight());
                    }
                }
                boxDo.setSkuTotalQty(dtoListBySeq.stream().map(NormalInboundImportDto::getPerBoxQty).reduce(0, Integer::sum));
                boxDo.setSkuKind(Math.toIntExact(dtoListBySeq.stream().map(NormalInboundImportDto::getSku).distinct().count()));
                List<OrderBoxProductDo> bpList = new ArrayList<>();
                //处理箱子内产品重复问题
                dtoListBySeq.stream().collect(Collectors.toMap(key -> key.getCompanyCode() + key.getSku(), val -> val, (v1, v2) -> {
                    v1.setBoxTotalQty(v1.getBoxTotalQty() + v2.getBoxTotalQty());
                    v1.setPerBoxQty(v1.getPerBoxQty() + v2.getPerBoxQty());
                    return v1;
                })).values().forEach(dto -> {
                    OrderBoxProductDo bpDo = new OrderBoxProductDo();
                    ProductDo productDo = companyIdSkuMatch.get(companyCodeMatch.get(dtoByTrackNo.getCompanyCode()).getId() + dto.getSku());
                    bpDo.setProductId(productDo.getId());
                    bpDo.setSku(productDo.getSku());
                    bpDo.setPerBoxQty(dto.getPerBoxQty());
                    bpList.add(bpDo);
                });
                List<OrderBoxDo> boxList = new ArrayList<>(dtoBySeq.getBoxTotalQty());
                List<OrderBoxProductDo> boxProductDoList = new ArrayList<>(dtoBySeq.getBoxTotalQty());
                for (int j = 1; j <= dtoBySeq.getBoxTotalQty(); j++) {
                    OrderBoxDo tempBoxDo = JSONUtil.toBean(JSONUtil.toJsonStr(boxDo), OrderBoxDo.class);
                    String boxMarkNumber = orderInputNo + "-S" + i.get() + "-" + j;
                    tempBoxDo.setBoxMarkNumber(boxMarkNumber);
                    boxList.add(tempBoxDo);
                    JSONUtil.toList(JSONUtil.toJsonStr(bpList), OrderBoxProductDo.class).forEach(item -> {
                        item.setBoxMarkNumber(boxMarkNumber);
                        boxProductDoList.add(item);
                    });
                }
                boxService.saveBatch(boxList);
                boxProductService.saveBatch(boxProductDoList);
                i.incrementAndGet();
            }
            //提交到货通知单后置操作
//            this.pushOrderInputToWmsAfterHandler(inputDo, inputDo.getId(), userId, username, false);
            res.setSuccess(res.getSuccess() + dtoListByTrackNo.size());
        }
        return res;
    }

    @Override
    public CommonBatchOperationVo importTransferInbound(MultipartFile file, Long tenantId, Long userId, String username) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<TransferInboundImportDto> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, TransferInboundImportDto.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        List<String> exceptionNoList = importDtoList.stream().filter(
                i -> StrUtil.isBlank(i.getNo())
                     || StrUtil.isBlank(i.getWarehouseCode())
                     || StrUtil.isBlank(i.getCompanyCode())
                     || StrUtil.isBlank(i.getArrivalMethod())
                     || StrUtil.isBlank(i.getBoxIdentificationNo())
                     || Objects.isNull(i.getBoxTotalQty())
                     || StrUtil.isBlank(i.getAddOrReuse())
        ).map(TransferInboundImportDto::getNo).collect(Collectors.toList());
        importDtoList = importDtoList.stream().filter(i -> !exceptionNoList.contains(i.getNo())).collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", "Exception");
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        Set<String> boxTypeNoSet = importDtoList.stream().map(TransferInboundImportDto::getBoxIdentificationNo).filter(StrUtil::isNotBlank).collect(Collectors.toSet());
        Map<String, OrderBoxDo> boxTypeExistMap = boxService.lambdaQuery()
                .in(OrderBoxDo::getBoxTypeNumber, CollUtil.isNotEmpty(boxTypeNoSet) ? boxTypeNoSet : Collections.singletonList("&*&*&*&*&*"))
                .groupBy(OrderBoxDo::getBoxTypeNumber)
                .list().stream().collect(Collectors.toMap(OrderBoxDo::getBoxTypeNumber, val -> val, (o1, o2) -> o1));
        Map<String, WarehouseDo> whCodeMatch = warehouseService.lambdaQuery()
                .eq(WarehouseDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(WarehouseDo::getWhCode, val -> val, (o1, o2) -> o1));
        Map<String, CompanyDo> companyCodeMatch = companyService.lambdaQuery()
                .eq(CompanyDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(CompanyDo::getCompanyCode, val -> val, (o1, o2) -> o1));
        Map<String, ProductDo> companyIdSkuMatch = productService.lambdaQuery()
                .eq(ProductDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(key -> key.getCompanyId() + key.getSku(), val -> val, (o1, o2) -> o1));
        //相同序号组成一个订单
        Map<String, List<TransferInboundImportDto>> dtoListByNoMap = importDtoList.stream().collect(Collectors.groupingBy(TransferInboundImportDto::getNo));
        for (Map.Entry<String, List<TransferInboundImportDto>> dtoEntry : dtoListByNoMap.entrySet()) {
            List<TransferInboundImportDto> dtoListByNo = dtoEntry.getValue();
            int orderRows = dtoListByNo.size();
            //校验数据
            TransferInboundImportDto dtoByNo = dtoListByNo.get(0);
            WarehouseDo warehouseDo = whCodeMatch.get(dtoByNo.getWarehouseCode());
            if (warehouseDo == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dtoByNo.getNo());
                map.put("errorMsg", "Warehouse Code/仓库代码[" + dtoByNo.getWarehouseCode() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + orderRows);
                res.getFailedList().add(map);
                continue;
            }
            CompanyDo companyDo = companyCodeMatch.get(dtoByNo.getCompanyCode());
            if (companyDo == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dtoByNo.getNo());
                map.put("errorMsg", "客户编码/Customer Code[" + dtoByNo.getCompanyCode() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + orderRows);
                res.getFailedList().add(map);
                continue;
            }
            Long companyId = companyDo.getId();
            if (ArrivalModeEnum.getCode(dtoByNo.getArrivalMethod()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dtoByNo.getNo());
                map.put("errorMsg", "到货方式/Arrival Mode[" + dtoByNo.getArrivalMethod() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + orderRows);
                res.getFailedList().add(map);
                continue;
            }
            OrderInputDo inputDo = BeanConvert.INSTANCE.transferInboundImportDtoToInputDo(dtoByNo);
            //生成入库单号
            String orderInputNo = this.generateOrderInputNo(InboundTypeEnum.two.getValue());
            inputDo.setSourceNo(orderInputNo);
            inputDo.setWarehouseId(warehouseDo.getId());
            inputDo.setCompanyId(companyId);
            inputDo.setArriveMethod(ArrivalModeEnum.getCode(dtoByNo.getArrivalMethod()));
            inputDo.setInboundType(InboundTypeEnum.two.getValue());
            inputDo.setStatus(OrderInputStatusEnum.zero.getValue());
            inputDo.setSkuTotalQty(0);
            inputDo.setBoxTotalQty(0);
            boolean passFlag = true;
            List<OrderBoxDo> boxDoList = new ArrayList<>();
            List<OrderBoxProductDo> boxProductDoList = new ArrayList<>();
            //根据箱类号分组
            Map<String, List<TransferInboundImportDto>> dtoListByBoxTypeMap = dtoListByNo.stream().collect(Collectors.groupingBy(TransferInboundImportDto::getBoxIdentificationNo));
            for (Map.Entry<String, List<TransferInboundImportDto>> boxTypeEntry : dtoListByBoxTypeMap.entrySet()) {
                List<TransferInboundImportDto> dtoListByBox = boxTypeEntry.getValue();
                TransferInboundImportDto dtoByBox = dtoListByBox.get(0);
                //箱数
                Integer boxQty = dtoByBox.getBoxTotalQty();
                //箱号
                String boxTypeNumber = dtoByBox.getBoxIdentificationNo();
                OrderBoxDo boxDo = new OrderBoxDo();
                boxDo.setBoxTypeNumber(boxTypeNumber);
                //查询箱信息
                OrderBoxDo oldBoxDo = boxTypeExistMap.get(boxTypeNumber);
                if ("Add".equals(dtoByBox.getAddOrReuse())) {
                    if (Objects.nonNull(oldBoxDo)) {
                        //已存在该箱号
                        HashMap<String, Object> map = new HashMap<>(4);
                        map.put("code", dtoByNo.getNo());
                        map.put("errorMsg", "Box Identification No./箱子标识号【" + boxTypeNumber + "】Already exists and cannot be added.");
                        res.setFailed(res.getFailed() + orderRows);
                        res.getFailedList().add(map);
                        passFlag = false;
                        break;
                    }
                    if (Objects.isNull(dtoByBox.getPackageLength())
                        || Objects.isNull(dtoByBox.getPackageWidth())
                        || Objects.isNull(dtoByBox.getPackageHeight())
                        || Objects.isNull(dtoByBox.getPackageWeight())
                        || dtoListByBox.stream().anyMatch(item -> StrUtil.isBlank(item.getSku()))
                        || dtoListByBox.stream().anyMatch(item -> Objects.isNull(item.getPerBoxQty()))) {
                        HashMap<String, Object> map = new HashMap<>(4);
                        map.put("code", dtoByNo.getNo());
                        map.put("errorMsg", StringsUtil.createI18Message(SysConstant.New_Box_Info_Partial));
                        res.setFailed(res.getFailed() + orderRows);
                        res.getFailedList().add(map);
                        passFlag = false;
                        break;
                    }
                    //新增箱判断产品是否存在
                    Optional<TransferInboundImportDto> skuCannotFind = dtoListByBox.stream().filter(i -> companyIdSkuMatch.get(companyId + i.getSku()) == null).findFirst();
                    if (skuCannotFind.isPresent()) {
                        HashMap<String, Object> map = new HashMap<>(4);
                        map.put("code", dtoByNo.getNo());
                        map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Company_Not_Exist_Sku, skuCannotFind.get().getCompanyCode(), skuCannotFind.get().getSku()));
                        res.setFailed(res.getFailed() + orderRows);
                        res.getFailedList().add(map);
                        passFlag = false;
                        break;
                    }
                    //region 处理箱信息
                    boxDo.setLength(dtoByBox.getPackageLength());
                    boxDo.setLengthBs(dtoByBox.getPackageLength().divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                    boxDo.setWidth(dtoByBox.getPackageWidth());
                    boxDo.setWidthBs(dtoByBox.getPackageWidth().divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                    boxDo.setHeight(dtoByBox.getPackageHeight());
                    boxDo.setHeightBs(dtoByBox.getPackageHeight().divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                    boxDo.setWeight(dtoByBox.getPackageWeight());
                    boxDo.setWeightBs(dtoByBox.getPackageWeight().multiply(BigDecimal.valueOf(2.2046)).setScale(3, RoundingMode.HALF_UP));
                    boxDo.setCustomizeBarcode(dtoByBox.getCustomizeBarcode());
                    if (StrUtil.isBlank(dtoByBox.getCustomizeBarcode())) {
                        if (dtoListByBox.size() == 1) {
                            ProductDo productDo = companyIdSkuMatch.get(companyId + dtoByBox.getSku());
                            if (productDo != null) {
                                boxDo.setCustomizeBarcode(productDo.getItf());
                            }
                        }
                    }
                    Integer boxInnerQty = dtoListByBox.stream().map(TransferInboundImportDto::getPerBoxQty).reduce(0, Integer::sum);
                    boxDo.setSkuTotalQty(boxQty * boxInnerQty);
                    boxDo.setBoxTotalQty(boxQty);
                    boxDo.setSkuKind(Math.toIntExact(dtoListByBox.stream().map(TransferInboundImportDto::getSku).distinct().count()));
                    //endregion
                    //region 处理箱内产品
                    List<OrderBoxProductDo> bpList = new ArrayList<>();
                    //处理箱子内产品重复问题
                    dtoListByBox.stream().collect(Collectors.toMap(key -> key.getCompanyCode() + key.getSku(), val -> val, (v1, v2) -> {
                        v1.setBoxTotalQty(v1.getBoxTotalQty() + v2.getBoxTotalQty());
                        v1.setPerBoxQty(v1.getPerBoxQty() + v2.getPerBoxQty());
                        return v1;
                    })).values().forEach(dto -> {
                        OrderBoxProductDo bpDo = new OrderBoxProductDo();
                        ProductDo productDo = companyIdSkuMatch.get(companyId + dto.getSku());
                        bpDo.setProductId(productDo.getId());
                        bpDo.setSku(productDo.getSku());
                        bpDo.setBoxTypeNumber(boxTypeNumber);
                        bpDo.setPerBoxQty(dto.getPerBoxQty());
                        bpList.add(bpDo);
                    });
                    boxProductDoList.addAll(bpList);
                    //endregion
                    inputDo.setSkuTotalQty(boxInnerQty * boxQty + inputDo.getSkuTotalQty());
                    inputDo.setBoxTotalQty(boxQty + inputDo.getBoxTotalQty());
                } else if ("Reuse".equals(dtoByBox.getAddOrReuse())) {
                    if (Objects.isNull(oldBoxDo)) {
                        HashMap<String, Object> map = new HashMap<>(4);
                        map.put("code", dtoByNo.getNo());
                        map.put("errorMsg", "Box Identification No./箱子标识号【" + boxTypeNumber + "】Does not exist. Please add it once before reusing it.");
                        res.setFailed(res.getFailed() + orderRows);
                        res.getFailedList().add(map);
                        passFlag = false;
                        break;
                    }
                    //region 处理箱信息
                    boxDo.setCustomizeBarcode(oldBoxDo.getCustomizeBarcode());
                    boxDo.setLength(oldBoxDo.getLength());
                    boxDo.setLengthBs(oldBoxDo.getLengthBs());
                    boxDo.setWidth(oldBoxDo.getWidth());
                    boxDo.setWidthBs(oldBoxDo.getWidthBs());
                    boxDo.setHeight(oldBoxDo.getHeight());
                    boxDo.setHeightBs(oldBoxDo.getHeightBs());
                    boxDo.setWeight(oldBoxDo.getWeight());
                    boxDo.setWeightBs(oldBoxDo.getWeightBs());
                    int boxInnerQty = oldBoxDo.getSkuTotalQty() / oldBoxDo.getBoxTotalQty();
                    boxDo.setSkuTotalQty(boxInnerQty * boxQty);
                    boxDo.setBoxTotalQty(boxQty);
                    boxDo.setSkuKind(oldBoxDo.getSkuKind());
                    //endregion
                    inputDo.setSkuTotalQty(boxInnerQty * boxQty + inputDo.getSkuTotalQty());
                    inputDo.setBoxTotalQty(boxQty + inputDo.getBoxTotalQty());
                } else {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dtoByNo.getNo());
                    map.put("errorMsg", "AddReuse/新增|复用 Wrong,Please choose between Add and Reuse！");
                    res.setFailed(res.getFailed() + orderRows);
                    res.getFailedList().add(map);
                    passFlag = false;
                    break;
                }
                boxDoList.add(boxDo);
            }
            if (!passFlag) {
                continue;
            }
            this.save(inputDo);
            createProgress(inputDo, username);
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Inbound_Order_Import,
                    "",
                    inputDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.OrderInput
            );
            boxDoList.forEach(item -> item.setOrderId(inputDo.getId()));
            boxService.saveBatch(boxDoList);
            boxProductService.saveBatch(boxProductDoList);
            res.setSuccess(res.getSuccess() + orderRows);
        }
        return res;
    }

    @Override
    public SingleResult updateTrackingNumber(InboundUpdateTrackingNumberBo bo, Long userId, String username) {
        OrderInputDo inputDo = this.getById(bo.getId());
        if (Objects.isNull(inputDo)) {
            return SingleResult.success(StringsUtil.createI18Message(SysConstant.Inbound_Not_Exist));
        }
        if (!inputDo.getStatus().equals(OrderInputStatusEnum.one.getValue()) && !inputDo.getStatus().equals(OrderInputStatusEnum.two.getValue())) {
            return SingleResult.success("入库单状态不正确,无法修改跟踪号");
        }
        this.lambdaUpdate()
                .eq(OrderInputDo::getId, bo.getId())
                .set(OrderInputDo::getTrackingNumber, bo.getTrackingNumber())
                .update();
        operateRecordService.operateAddRecordAndValue(
                SysConstant.OrderInput_Update_Tracking_Number,
                "",
                bo.getId(),
                userId,
                username,
                OperateTypeEnum.OrderInput
        );
        return SingleResult.success("更新成功");
    }

    @Override
    public void forceEnd(Long id, Long userId, String username) {
        this.receiptFinish(id, userId, username, true);
        boxService.lambdaUpdate()
                .eq(OrderBoxDo::getOrderId, id)
                .isNull(OrderBoxDo::getReceivedTime)
                .set(OrderBoxDo::getReceivedTime, DateUtil.date())
                .update();
    }

    /**
     * 构建出适合合并的数据结构(收货单)
     *
     * @param dtoList 通用的预构数据
     * @return 自定义合并策略需要的数据
     */
    private DynamicTableDto getReceivingNoteTableData(List<MultiLevelLoopRowTableDto> dtoList, DynamicTableDto dynamicTableDto) {
        for (MultiLevelLoopRowTableDto dto : dtoList) {
            dynamicTableDto.getGroupMap().put(dto.getNo(), dto.getSonList().size());
            for (Map<String, String> sonMap : dto.getSonList()) {
                RowRenderData rowRenderData = Rows.of(
                        dto.getNo(),
                        dto.getType(),
                        dto.getQty(),
                        sonMap.get("sku"),
                        sonMap.get("skuQty"),
                        sonMap.get("receivedSkuQty")
                ).center().horizontalCenter().create();
                dynamicTableDto.getDataList().add(rowRenderData);
            }
        }
        return dynamicTableDto;
    }

    /**
     * 构建出适合合并的数据结构(收货单 箱条码)
     *
     * @param dtoList 通用的预构数据
     * @return 自定义合并策略需要的数据
     */
    private DynamicTableDto getReceivingNoteBoxBarcodeTableData(List<MultiLevelLoopRowTableDto> dtoList, DynamicTableDto dynamicTableDto) {
        for (MultiLevelLoopRowTableDto dto : dtoList) {
            dynamicTableDto.getGroupMap().put(dto.getNo(), dto.getSonList().size());
            ByteArrayInputStream boxBarcodeImg = null;
            if (StrUtil.isNotBlank(dto.getType())) {
                //入库单号条形
                byte[] boxBarcode = BarCodeUtils.generateBarCode128(
                        dto.getType(),
                        false,
                        false,
                        256,
                        15,
                        0.4f,
                        0,
                        0
                );
                boxBarcodeImg = new ByteArrayInputStream(boxBarcode);
            }
            for (Map<String, String> sonMap : dto.getSonList()) {
                RowRenderData rowRenderData = Rows.of(
                        Cells.of(dto.getNo()).create(),
                        Objects.isNull(boxBarcodeImg) ? Cells.of("-").create() : Cells.of(Pictures.ofStream(boxBarcodeImg, PictureType.PNG).size(200, 55).create()).create(),
                        Cells.of(dto.getQty()).create(),
                        Cells.of(sonMap.get("sku")).create(),
                        Cells.of(sonMap.get("skuQty")).create(),
                        Cells.of(sonMap.get("receivedSkuQty")).create()
                ).center().horizontalCenter().create();
                dynamicTableDto.getDataList().add(rowRenderData);
            }
        }
        return dynamicTableDto;
    }

    /**
     * 备货中转入库单
     */
    private Map<String, Object> getTransferExportList(
            List<OrderInputListVo> list,
            Map<Long, String> companyIdMatch,
            Map<Long, String> whIdMatch,
            WmsSystemEntity wmsSet,
            OmpSystemEntity ompSet,
            OmsSystemEntity omsSet) {
        ExportParams exportParams = new ExportParams();
        //设置样式
        exportParams.setStyle(ExcelExportTitleStyle.class);
        // 设置sheet的名称
        exportParams.setSheetName("备货中转入库单");
        List<TransferInboundExportDto> exportList = new ArrayList<>();
        List<OrderInputListVo> dtoList = list.stream()
                .filter(item -> InboundTypeEnum.two.getValue().equals(item.getInboundType()))
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(dtoList)) {
            //入库单号&入库单id
            List<String> sourceNoList = dtoList.stream().map(OrderInputListVo::getSourceNo).collect(Collectors.toList());
            List<Long> idList = dtoList.stream().map(OrderInputListVo::getId).collect(Collectors.toList());
            //入库单id匹配箱子
            Map<Long, List<OrderBoxDo>> boxListMatch = boxService.lambdaQuery()
                    .in(OrderBoxDo::getOrderId, idList)
                    .list().stream().collect(Collectors.groupingBy(OrderBoxDo::getOrderId));
            //箱类号匹配箱子内产品信息
            Map<String, List<OrderBoxProductDo>> boxTypeNumberMatch = boxProductService.lambdaQuery()
                    .in(OrderBoxProductDo::getBoxTypeNumber, boxListMatch.values().stream().flatMap(Collection::stream).map(OrderBoxDo::getBoxTypeNumber).collect(Collectors.toSet()))
                    .list().stream().collect(Collectors.groupingBy(OrderBoxProductDo::getBoxTypeNumber));
            //入库单匹配进行过上架操作的上架单
            Map<String, List<OrderInputShelfDo>> shelfListMatch = shelfService.lambdaQuery()
                    .in(OrderInputShelfDo::getInboundSourceNo, sourceNoList)
                    .ne(OrderInputShelfDo::getStatus, OrderInputShelfStatusEnum.zero.getValue())
                    .list().stream().collect(Collectors.groupingBy(OrderInputShelfDo::getInboundSourceNo));
            //上架单id匹配库存信息
            Map<Long, List<ProductStockDo>> pasIdMatchStock = productStockService.lambdaQuery()
                    .eq(ProductStockDo::getOrderType, StockOrderTypeEnum.Shelf.getValue())
                    .in(CollUtil.isNotEmpty(shelfListMatch), ProductStockDo::getOrderId, shelfListMatch.values().stream().flatMap(Collection::stream).map(OrderInputShelfDo::getId).collect(Collectors.toList()))
                    .eq(ProductStockDo::getType, StockTypeEnum.two.getValue())
                    .list().stream().collect(Collectors.groupingBy(ProductStockDo::getOrderId));
            dtoList.forEach(item -> {
                //查询上架信息
                List<ProductStockDo> stockList = new ArrayList<>();
                if (CollUtil.isNotEmpty(shelfListMatch)) {
                    List<OrderInputShelfDo> orderInputShelfDos = shelfListMatch.get(item.getSourceNo());
                    if (CollUtil.isNotEmpty(orderInputShelfDos)) {
                        orderInputShelfDos.stream()
                                .map(OrderInputShelfDo::getId)
                                .forEach(pasId -> {
                                    List<ProductStockDo> productStockDoList = pasIdMatchStock.get(pasId);
                                    if (CollUtil.isNotEmpty(productStockDoList)) {
                                        stockList.addAll(productStockDoList);
                                    }
                                });
                    }
                }
                Map<String, ProductStockDo> boxTypeNoMatchShelf = stockList.stream().collect(Collectors.toMap(ProductStockDo::getBoxTypeNumber, val -> val, (o1, o2) -> {
                    o1.setReceiveQuantity(o1.getReceiveQuantity() + o2.getReceiveQuantity());
                    return o1;
                }));
                List<OrderBoxDo> boxList = boxListMatch.get(item.getId());
                //合并箱类号
                boxList = new ArrayList<>(
                        boxList.stream()
                                .collect(Collectors.toMap(OrderBoxDo::getBoxTypeNumber, val -> val, (o1, o2) -> {
                                    o1.setBoxTotalQty(o1.getBoxTotalQty() + o2.getBoxTotalQty());
                                    o1.setReceivedBoxQty(o1.getReceivedBoxQty() + o2.getReceivedBoxQty());
                                    return o1;
                                })).values()
                );
                for (OrderBoxDo boxDo : boxList) {
                    //箱子内产品
                    List<OrderBoxProductDo> bpList = boxTypeNumberMatch.get(boxDo.getBoxTypeNumber());
                    //根据sku循环
                    for (int skuIndex = 0; skuIndex < bpList.size(); skuIndex++) {
                        OrderBoxProductDo bpDo = bpList.get(skuIndex);
                        //根据箱数循环
                        Integer receivedBoxQty = boxDo.getReceivedBoxQty() == 0 ? boxDo.getBoxTotalQty() : boxDo.getReceivedBoxQty();
                        for (int boxNumIndex = 0; boxNumIndex < receivedBoxQty; boxNumIndex++) {
//                            TransferInboundExportDto dto = new TransferInboundExportDto();
//                            BeanUtils.copyProperties(item, dto);
                            TransferInboundExportDto dto = BeanConvert.INSTANCE.inputListVoToTransferInboundExportDto(item);
                            dto.setWarehouseNameCode(whIdMatch.get(item.getWarehouseId()));
                            dto.setCompanyNameCode(companyIdMatch.get(item.getCompanyId()));
                            dto.setStatusName(OrderInputStatusEnum.getDesc(item.getStatus()));
                            dto.setBoxTypeNumber(boxDo.getBoxTypeNumber());
                            dto.setCustomizeBarcode(boxDo.getCustomizeBarcode());
                            if (StaticDict.OMP_UnitMeasurement.Cm.getValue().equals(Objects.nonNull(wmsSet) ? wmsSet.getUnitMeasurement().getUnit() : Objects.nonNull(ompSet) ? ompSet.getUnitMeasurement().getUnit() : omsSet.getUnitMeasurement().getUnit())) {
                                dto.setLength(boxDo.getLength());
                                dto.setWidth(boxDo.getWidth());
                                dto.setHeight(boxDo.getHeight());
                                dto.setOmsMeasureUnit("cm");
                                dto.setWeight(boxDo.getWeight());
                                dto.setOmsWeightUnit("kg");
                                dto.setWmsLength(boxDo.getLength());
                                dto.setWmsWidth(boxDo.getWidth());
                                dto.setWmsHeight(boxDo.getHeight());
                                dto.setWmsMeasureUnit("cm");
                                dto.setWmsWeight(boxDo.getWeight());
                                dto.setWmsWeightUnit("kg");
                            } else {
                                dto.setLength(boxDo.getLengthBs());
                                dto.setWidth(boxDo.getWidthBs());
                                dto.setHeight(boxDo.getHeightBs());
                                dto.setOmsMeasureUnit("in");
                                dto.setWeight(boxDo.getWeightBs());
                                dto.setOmsWeightUnit("lb");
                                dto.setWmsLength(boxDo.getLengthBs());
                                dto.setWmsWidth(boxDo.getWidthBs());
                                dto.setWmsHeight(boxDo.getHeightBs());
                                dto.setWmsMeasureUnit("in");
                                dto.setWmsWeight(boxDo.getWeightBs());
                                dto.setWmsWeightUnit("lb");
                            }
                            if (skuIndex == 0 && boxNumIndex == 0) {
                                dto.setReceiptPalletsQty(item.getReceiptPalletsQty());
                                dto.setPalletsTotalQty(item.getPalletsTotalQty());
                                dto.setForecastBoxQty(boxDo.getBoxTotalQty());
                                dto.setReceiptBoxQty(boxDo.getReceivedBoxQty());
                                ProductStockDo stockDo = boxTypeNoMatchShelf.get(boxDo.getBoxTypeNumber());
                                if (Objects.nonNull(stockDo)) {
                                    dto.setShelfBoxQty(stockDo.getReceiveQuantity());
                                    dto.setUnShelfBoxQty(boxDo.getReceivedBoxQty() - stockDo.getReceiveQuantity());
                                } else {
                                    dto.setShelfBoxQty(0);
                                    dto.setUnShelfBoxQty(receivedBoxQty);
                                }
                            } else {
                                dto.setReceiptPalletsQty(null);
                                dto.setPalletsTotalQty(null);
                                dto.setForecastBoxQty(null);
                                dto.setReceiptBoxQty(null);
                                dto.setShelfBoxQty(null);
                                dto.setUnShelfBoxQty(null);
                            }
                            dto.setSku(bpDo.getSku());
                            if (boxNumIndex == 0) {
                                dto.setSkuTotalQty(receivedBoxQty * bpDo.getPerBoxQty());
                            } else {
                                dto.setSkuTotalQty(null);
                            }
                            dto.setPerBoxQty(bpDo.getPerBoxQty());
                            exportList.add(dto);
                        }
                    }
                }
            });
        }
        Map<String, Object> map = new HashMap<>(8);
        // title的参数为ExportParams类型，目前仅仅在ExportParams中设置了sheetName
        map.put("title", exportParams);
        // 模版导出对应得实体类型，即包含了List的对象
        map.put("entity", TransferInboundExportDto.class);
        // sheet中要填充得数据
        map.put("data", exportList);
        return map;
    }

    /**
     * 入库单（按箱唛）
     */
    private Map<String, Object> getNormalBoxExportList(
            List<OrderInputListVo> normalDtoList,
            Map<Long, String> companyIdMatch,
            Map<Long, String> whIdMatch,
            Map<Long, List<OrderBoxDo>> orderIdMatchBoxList,
            Map<String, List<OrderBoxProductDo>> boxMarkNoMatchBpList,
            Map<Long, ProductDo> productIdMatch,
            WmsSystemEntity wmsSet,
            OmpSystemEntity ompSet,
            OmsSystemEntity omsSet) {
        ExportParams exportParams = new ExportParams();
        //设置样式
        exportParams.setStyle(ExcelExportTitleStyle.class);
        // 设置sheet的名称
        exportParams.setSheetName("一件代发入库单（按箱唛）");
        List<NormalInboundByBoxExportDto> exportList = new ArrayList<>();
        if (CollUtil.isNotEmpty(normalDtoList)) {
            normalDtoList.forEach(item -> {
                List<OrderBoxDo> boxList = orderIdMatchBoxList.get(item.getId());
                for (int i = 0; i < boxList.size(); i++) {
                    OrderBoxDo boxDo = boxList.get(i);
                    List<OrderBoxProductDo> bpList = boxMarkNoMatchBpList.get(boxDo.getBoxMarkNumber());
                    for (int j = 0; j < bpList.size(); j++) {
                        OrderBoxProductDo bpDo = bpList.get(j);
//                        NormalInboundByBoxExportDto dto = new NormalInboundByBoxExportDto();
//                        BeanUtils.copyProperties(item, dto);
                        NormalInboundByBoxExportDto dto = BeanConvert.INSTANCE.inputListVoToNormalInboundByBoxExportDto(item);
                        dto.setWarehouseNameCode(whIdMatch.get(item.getWarehouseId()));
                        dto.setCompanyNameCode(companyIdMatch.get(item.getCompanyId()));
                        dto.setStatusName(OrderInputStatusEnum.getDesc(item.getStatus()));
                        if (i == 0 && j == 0) {
                            dto.setReceiptPalletsQty(item.getReceiptPalletsQty());
                            dto.setPalletsTotalQty(item.getPalletsTotalQty());
                            dto.setBoxTotalQty(item.getBoxTotalQty());
                            dto.setReceiptTotalBoxQty(item.getReceiptBoxQty());
                        } else {
                            dto.setReceiptPalletsQty(null);
                            dto.setPalletsTotalQty(null);
                            dto.setBoxTotalQty(null);
                            dto.setReceiptTotalBoxQty(null);
                        }
                        dto.setBoxMarkNumber(boxDo.getBoxMarkNumber());
                        dto.setCustomizeBarcode(boxDo.getCustomizeBarcode());
                        if (j == 0) {
                            dto.setReceiptBoxQty(Objects.nonNull(boxDo.getReceivedTime()) ? 1 : 0);
                        } else {
                            dto.setReceiptBoxQty(null);
                        }
                        if (StaticDict.OMP_UnitMeasurement.Cm.getValue().equals(Objects.nonNull(wmsSet) ? wmsSet.getUnitMeasurement().getUnit() : Objects.nonNull(ompSet) ? ompSet.getUnitMeasurement().getUnit() : omsSet.getUnitMeasurement().getUnit())) {
                            dto.setLength(boxDo.getLength());
                            dto.setWidth(boxDo.getWidth());
                            dto.setHeight(boxDo.getHeight());
                            dto.setOmsMeasureUnit("cm");
                            dto.setWeight(boxDo.getWeight());
                            dto.setOmsWeightUnit("kg");
                        } else {
                            dto.setLength(boxDo.getLengthBs());
                            dto.setWidth(boxDo.getWidthBs());
                            dto.setHeight(boxDo.getHeightBs());
                            dto.setOmsMeasureUnit("in");
                            dto.setWeight(boxDo.getWeightBs());
                            dto.setOmsWeightUnit("lb");
                        }
                        dto.setSku(bpDo.getSku());
                        dto.setProductName(productIdMatch.getOrDefault(bpDo.getProductId(), new ProductDo()).getProductName());
                        dto.setForecastQty(bpDo.getPerBoxQty());
                        dto.setReceiptQty(bpDo.getReceiptQty());
                        exportList.add(dto);
                    }
                }
            });
        }
        Map<String, Object> map = new HashMap<>(8);
        // title的参数为ExportParams类型，目前仅仅在ExportParams中设置了sheetName
        map.put("title", exportParams);
        // 模版导出对应得实体类型，即包含了List的对象
        map.put("entity", NormalInboundByBoxExportDto.class);
        // sheet中要填充得数据
        map.put("data", exportList);
        return map;
    }

    /**
     * 入库单（按SKU）
     */
    private Map<String, Object> getNormalSkuExportList(
            List<OrderInputListVo> normalDtoList,
            Map<Long, String> companyIdMatch,
            Map<Long, String> whIdMatch,
            Map<Long, List<OrderBoxDo>> orderIdMatchBoxList,
            Map<String, List<OrderBoxProductDo>> boxMarkNoMatchBpList,
            Map<Long, ProductDo> productIdMatch,
            WmsSystemEntity wmsSet,
            OmpSystemEntity ompSet,
            OmsSystemEntity omsSet) {
        ExportParams exportParams = new ExportParams();
        //设置样式
        exportParams.setStyle(ExcelExportTitleStyle.class);
        // 设置sheet的名称
        exportParams.setSheetName("一件代发入库单（按SKU）");
        List<NormalInboundBySkuExportDto> exportList = new ArrayList<>();
        if (CollUtil.isNotEmpty(normalDtoList)) {
            normalDtoList.forEach(item -> {
                //查询上架数据
                //获取入库单所有上架单id
                List<Long> pasIdList = shelfService.lambdaQuery()
                        .eq(OrderInputShelfDo::getInboundSourceNo, item.getSourceNo())
                        .list().stream()
                        .map(OrderInputShelfDo::getId)
                        .collect(Collectors.toList());
                Collection<NormalInputShelfDto> shelfData = new ArrayList<>();
                if (CollUtil.isNotEmpty(pasIdList)) {
                    shelfData = productStockService.getNormalInputShelfData(pasIdList);
                }
                List<OrderBoxDo> boxList = orderIdMatchBoxList.get(item.getId());
                List<OrderBoxProductDo> bpList = new ArrayList<>();
                boxList.forEach(box -> bpList.addAll(boxMarkNoMatchBpList.get(box.getBoxMarkNumber())));
                //避免toMap合并相同sku时，影响原数据
                List<OrderBoxProductDo> bpListCopy = JSONUtil.toList(JSONUtil.toJsonStr(bpList), OrderBoxProductDo.class);
                //合并sku
                Map<String, OrderBoxProductDo> skuMap = bpListCopy.stream().collect(Collectors.toMap(OrderBoxProductDo::getSku, val -> val, (o1, o2) -> {
                    o1.setPerBoxQty(o1.getPerBoxQty() + o2.getPerBoxQty());
                    o1.setReceiptQty(o1.getReceiptQty() + o2.getReceiptQty());
                    return o1;
                }));
                List<OrderBoxProductDo> boxProductList = new ArrayList<>(skuMap.values());
                for (int i = 0; i < boxProductList.size(); i++) {
                    OrderBoxProductDo boxProductDo = boxProductList.get(i);
                    String sku = boxProductDo.getSku();
                    Long productId = boxProductDo.getProductId();
//                    NormalInboundBySkuExportDto dto = new NormalInboundBySkuExportDto();
//                    BeanUtils.copyProperties(item, dto);
                    NormalInboundBySkuExportDto dto = BeanConvert.INSTANCE.inputListVoToNormalInboundBySkuExportDto(item);
                    dto.setWarehouseNameCode(whIdMatch.get(item.getWarehouseId()));
                    dto.setCompanyNameCode(companyIdMatch.get(item.getCompanyId()));
                    dto.setStatusName(OrderInputStatusEnum.getDesc(item.getStatus()));
                    if (i == 0) {
                        dto.setBoxTotalQty(item.getBoxTotalQty());
                        dto.setReceiptBoxQty(item.getReceiptBoxQty());
                        dto.setPalletsTotalQty(item.getPalletsTotalQty());
                        dto.setReceiptPalletsQty(item.getReceiptPalletsQty());
                    } else {
                        dto.setBoxTotalQty(null);
                        dto.setReceiptBoxQty(null);
                        dto.setPalletsTotalQty(null);
                        dto.setReceiptPalletsQty(null);
                    }
                    ProductDo productDo = productIdMatch.get(productId);
                    if (productDo == null) {
                        productDo = new ProductDo();
                    }
                    dto.setSku(sku);
                    dto.setProductName(productDo.getProductName());
                    if (StaticDict.OMP_UnitMeasurement.Cm.getValue().equals(Objects.nonNull(wmsSet) ? wmsSet.getUnitMeasurement().getUnit() : Objects.nonNull(ompSet) ? ompSet.getUnitMeasurement().getUnit() : omsSet.getUnitMeasurement().getUnit())) {
                        dto.setLength(productDo.getLength());
                        dto.setWidth(productDo.getWidth());
                        dto.setHeight(productDo.getHeight());
                        dto.setOmsMeasureUnit("cm");
                        dto.setWeight(productDo.getWeight());
                        dto.setOmsWeightUnit("kg");
                        dto.setWmsLength(productDo.getWmsLength());
                        dto.setWmsWidth(productDo.getWmsWidth());
                        dto.setWmsHeight(productDo.getWmsHeight());
                        dto.setWmsMeasureUnit("cm");
                        dto.setWmsWeight(productDo.getWmsWeight());
                        dto.setWmsWeightUnit("kg");
                    } else {
                        dto.setLength(productDo.getLengthBs());
                        dto.setWidth(productDo.getWidthBs());
                        dto.setHeight(productDo.getHeightBs());
                        dto.setOmsMeasureUnit("in");
                        dto.setWeight(productDo.getWeightBs());
                        dto.setOmsWeightUnit("lb");
                        dto.setWmsLength(productDo.getWmsLengthBs());
                        dto.setWmsWidth(productDo.getWmsWidthBs());
                        dto.setWmsHeight(productDo.getWmsHeightBs());
                        dto.setWmsMeasureUnit("in");
                        dto.setWmsWeight(productDo.getWmsWeightBs());
                        dto.setWmsWeightUnit("lb");
                    }
                    dto.setForecastQty(boxProductDo.getPerBoxQty());
                    dto.setReceiptQty(boxProductDo.getReceiptQty());
                    //应上架数
                    Integer shouldQty = boxProductDo.getReceiptQty() == 0
                            ? boxProductDo.getPerBoxQty()
                            : boxProductDo.getReceiptQty();
                    if (CollUtil.isNotEmpty(shelfData)) {
                        dto.setShelfQty(
                                shelfData.stream()
                                        .filter(shelf -> productId.equals(shelf.getProductId()))
                                        .map(NormalInputShelfDto::getReceiptQty)
                                        .reduce(0, Integer::sum)
                        );
                        dto.setUnShelfQty(shouldQty - dto.getShelfQty());
                    } else {
                        dto.setShelfQty(0);
                        dto.setUnShelfQty(shouldQty);
                    }
                    exportList.add(dto);
                }
            });
        }
        Map<String, Object> map = new HashMap<>(8);
        // title的参数为ExportParams类型，目前仅仅在ExportParams中设置了sheetName
        map.put("title", exportParams);
        // 模版导出对应得实体类型，即包含了List的对象
        map.put("entity", NormalInboundBySkuExportDto.class);
        // sheet中要填充得数据
        map.put("data", exportList);
        return map;
    }

    @Override
    public void exportSkuLabel(SkuCodePrintPdfBo bo, HttpServletResponse response) throws IOException {
        int width = bo.getWidth();
        int length = bo.getLength();
        Map<String, String> map = bo.getSourceMap();
        float pageHeight = width * (MathUtils.divBig(3, 842, 297).floatValue());
        float pageLength = length * (MathUtils.divBig(3, 595, 210).floatValue());
        OrderInputDo orderInputDo = getById(bo.getOrderId());
        // 入库单id
        // List<Long> idList = normalDtoList.stream().map(OrderInputListVo::getId).distinct().collect(Collectors.toList());
        //入库单id匹配箱子
        List<OrderBoxDo> orderBoxList = boxService.lambdaQuery()
                .eq(OrderBoxDo::getOrderId, bo.getOrderId())
                .list();
        //箱唛号匹配箱子内产品信息
        List<OrderBoxProductDo> boxMarkNoMatchBpList = boxProductService.lambdaQuery()
                .in(orderBoxList.stream().map(OrderBoxDo::getBoxMarkNumber).anyMatch(StringUtils::isNotEmpty), OrderBoxProductDo::getBoxMarkNumber,
                        orderBoxList.stream().map(OrderBoxDo::getBoxMarkNumber).collect(Collectors.toList()))
                .in(orderBoxList.stream().map(OrderBoxDo::getBoxTypeNumber).anyMatch(StringUtils::isNotEmpty), OrderBoxProductDo::getBoxTypeNumber,
                        orderBoxList.stream().map(OrderBoxDo::getBoxTypeNumber).collect(Collectors.toList())
                )
                .list();
        //查询所有产品信息
        Map<Long, Integer> skuCountMap = boxMarkNoMatchBpList.stream().collect(Collectors.groupingBy(OrderBoxProductDo::getProductId, Collectors.summingInt(OrderBoxProductDo::getPerBoxQty)));
        //换算尺寸
        Document doc = new Document();
        doc.setPageSize(new Rectangle(pageLength, pageHeight));
        doc.setMargins(4, 4, 4, 4);
        try {
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(orderInputDo.getSourceNo() + ".pdf", "UTF-8"));
            PdfWriter writer = PdfWriter.getInstance(doc, response.getOutputStream());
            // File file=new File("E:\\nginx-1.16.0\\file0\\12139.pdf");
            // FileOutputStream fileOutputStream=new FileOutputStream(file);
            // PdfWriter writer = PdfWriter.getInstance(doc, fileOutputStream);
            doc.open();
            String codeType = "";
            for (Long prodcutId : skuCountMap.keySet()) {
                ProductDo productDo = productService.getById(prodcutId);
                Map<String, String> attributeMap = new TreeMap<>();
                if (org.apache.commons.lang.StringUtils.isNotBlank(map.get("skuType"))) {
                    if ("sku".equals(map.get("skuType"))) {
                        attributeMap.put("2sku", productDo.getSku());

                    } else if ("skuCode".equals(map.get("skuType"))) {
                        attributeMap.put("2sku", productDo.getMainBarcode());

                    } else if ("skuId".equals(map.get("skuType"))) {
                        attributeMap.put("2sku", productDo.getSku());
                    }
                }
                if (org.apache.commons.lang.StringUtils.isNotBlank(map.get("codeType"))) {
                    codeType = map.get("codeType");
                }
                if (org.apache.commons.lang.StringUtils.isNotBlank(map.get("productName"))) {

                    if ("productName".equals(map.get("productName"))) {
                        attributeMap.put("1productName", productDo.getProductName());
                    } else if ("productAlias".equals(map.get("productName"))) {
                        attributeMap.put("1productName", productDo.getProductAliasName());
                    }
                }
                if (org.apache.commons.lang.StringUtils.isNotBlank(map.get("new"))) {
                    attributeMap.put("3new", "New");
                }
                if (org.apache.commons.lang.StringUtils.isNotBlank(map.get("companyCode"))) {
                    attributeMap.put("4companyCode", map.get("companyCode"));
                }
                if (org.apache.commons.lang.StringUtils.isNotBlank(map.get("madeInChina"))) {
                    attributeMap.put("5madeInChina", "Made IN China");
                }
                for (int j = 0; j < skuCountMap.get(prodcutId); j++) {
                    //因为打印的是一张 一个条形码， 不考虑padding 默认 -1
                    PdfPTable barTable = BarCodePdfUtils.getOneTableNew(codeType, attributeMap, writer, length, pageLength, "other");
//                    PdfPTable barTable1 = new PdfPTable(1);//table 里面分很多个 小table 一个小table 放 1个条形码
//                    barTable1.setWidthPercentage(100);
////                    barTable1.getDefaultCell().setBorder(Rectangle.NO_BORDER);
//                    barTable1.addCell(barTable);
//                    barTable1.getDefaultCell().setBorder(Rectangle.NO_BORDER);
//                    barTable1.getDefaultCell().setVerticalAlignment(Element.ALIGN_CENTER);
//                    barTable1.getDefaultCell().setFixedHeight(pageHeight - 1);
                    doc.add(barTable);
                    doc.newPage();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            doc.close();
        }
    }


    @Override
    public void printPackageLabelPdf(PackageLabelPrintPdfBo bo, HttpServletResponse response) {
        int width = bo.getSize();
        int length = 100;
        // Map<String, String> map = bo.getSourceMap();
        float pageHeight = width * (MathUtils.divBig(3, 842, 297).floatValue());
        float pageLength = length * (MathUtils.divBig(3, 595, 210).floatValue());
        if (bo.getStartPackage() > bo.getEndPackage()) {
            bo.setStartPackage(bo.getEndPackage());
        }
        OrderInputDo orderInputDo = getById(bo.getOrderId());
        CompanyDo companyDo = companyService.getById(orderInputDo.getCompanyId());
        String wh = "";
        String company = "";
        if (companyDo != null) {
            company = companyDo.getCompanyName() + "(" + companyDo.getCompanyCode() + ")";
        }
        WarehouseDo warehouseDo = warehouseService.getById(orderInputDo.getWarehouseId());
        if (warehouseDo != null) {
            wh = warehouseDo.getWhNameCn() + "(" + warehouseDo.getWhCode() + ")";
        }
        // 入库单id
        // List<Long> idList = normalDtoList.stream().map(OrderInputListVo::getId).distinct().collect(Collectors.toList());
        //入库单id匹配箱子
        List<OrderBoxDo> orderBoxList = boxService.lambdaQuery()
                .eq(OrderBoxDo::getOrderId, bo.getOrderId())
                .list();

        //换算尺寸
        Document doc = new Document();
        doc.setPageSize(new Rectangle(pageLength, pageHeight));
        doc.setMargins(4, 4, 4, 0);
        try {
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(orderInputDo.getSourceNo() + ".pdf", "UTF-8"));
            PdfWriter writer = PdfWriter.getInstance(doc, response.getOutputStream());
            // File file=new File("E:\\nginx-1.16.0\\file0\\12139.pdf");
            // FileOutputStream fileOutputStream=new FileOutputStream(file);
            // PdfWriter writer = PdfWriter.getInstance(doc, fileOutputStream);
            doc.open();
            for (int i = 1; i < orderBoxList.size() + 1; i++) {
                OrderBoxDo orderBoxDo = orderBoxList.get(i - 1);
                if (i < bo.getStartPackage() || i > bo.getEndPackage()) {
                    continue;
                }
                //箱唛号匹配箱子内产品信息
                SFunction<OrderBoxProductDo, String> bpFunction = StrUtil.isNotBlank(orderBoxDo.getBoxMarkNumber()) ? OrderBoxProductDo::getBoxMarkNumber : OrderBoxProductDo::getBoxTypeNumber;
                String boxNo = StrUtil.isNotBlank(orderBoxDo.getBoxMarkNumber()) ? orderBoxDo.getBoxMarkNumber() : orderBoxDo.getBoxTypeNumber();
                List<OrderBoxProductDo> boxMarkNoMatchBpList = boxProductService.lambdaQuery()
                        .eq(bpFunction, boxNo)
                        .list().stream().map(orderBoxProductDo -> {
                            ProductDo productDo = productService.getById(orderBoxProductDo.getProductId());
                            orderBoxProductDo.setBoxMarkNumber(productDo.getProductName());
                            return orderBoxProductDo;
                        }).collect(Collectors.toList());
                PdfPTable table = BarCodePdfUtils.createPackageLabel(writer, orderInputDo.getSourceNo(), orderInputDo.getCreateTime(), wh, company, orderBoxDo, boxMarkNoMatchBpList, width, bo.getCodeType(), bo.getMadeInChina(), bo.getProductName());
                doc.add(table);
                doc.newPage();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            doc.close();
        }
    }

    /**
     * 修改上架单实收托盘数日志
     */
    private void updatePalletQtyLog(Long id, Long userId, String username) {
        //日志
        operateRecordService.operateAddRecordAndValue(
                SysConstant.OrderInput_Update_Receipt_PalletsQty,
                "",
                id,
                userId,
                username,
                OperateTypeEnum.OrderInput
        );
    }

    @Override
    public PageResult<OrderInputListVo> getListPage(OrderInputQueryBo bo) {
        Integer current = bo.getCurrent();
        Integer pageSize = bo.getPageSize();
        PageHelper.startPage(current, pageSize);
        List<OrderInputListVo> list = baseMapper.getListPage(bo);
        PageInfo<OrderInputListVo> pageInfo = new PageInfo<>(list);
        if ("oms".equals(bo.getSource())) {
            //获取计费
            List<Long> collect = pageInfo.getList().stream().map(OrderInputListVo::getId).collect(Collectors.toList());
            Map<Long, List<CostItemForOrderPageDto>> itemMap = costService.getCostItem(collect,
                            StaticDict.Cost_Input_OrderType.Second.getValue() + "___" + StaticDict.Cost_Input_OrderType.Third.getValue(), bo.getTenantId())
                    .stream().collect(Collectors.groupingBy(CostItemForOrderPageDto::getOrderId));
            for (OrderInputListVo et : pageInfo.getList()) {
                et.setBillItems(itemMap.get(et.getId()));
            }
        }

        return new PageResult<>(
                this.handlerListAfter(list),
                current,
                pageSize,
                (int) pageInfo.getTotal(),
                pageInfo.getPages()
        );
    }

    @Override
    public PageResult getListPageApi(OrderInputQueryBo bo) {
        Integer current = bo.getCurrent();
        Integer pageSize = bo.getPageSize();
        PageHelper.startPage(current, pageSize);
        List<OrderInputListVo> list = baseMapper.getListPage(bo);
        PageInfo<OrderInputListVo> pageInfo = new PageInfo<>(list);
        List<OrderInputApiListVo> listApiVoList = BeanCopierUtils.listCopyMapper(list, OrderInputListVo.class, OrderInputApiListVo.class);
        return new PageResult<>(
                this.handlerApiListAfter(listApiVoList),
                current,
                pageSize,
                (int) pageInfo.getTotal(),
                pageInfo.getPages()
        );
    }

    /**
     * 查询分页列表之后对列表数据的处理
     */
    private List<OrderInputApiListVo> handlerApiListAfter(List<OrderInputApiListVo> list) {
        if (CollUtil.isEmpty(list)) {
            return list;
        }
        Map<Long, List<OrderInputListApiBoxInfoVo>> orderIdMatchProducts = baseMapper.getBoxProductListApi(list.stream().map(OrderInputApiListVo::getId).collect(Collectors.toList())).stream().collect(Collectors.groupingBy(OrderInputListApiBoxInfoVo::getOrderId));
        for (OrderInputApiListVo apiListVo : list) {
            apiListVo.setBoxInfoList(orderIdMatchProducts.getOrDefault(apiListVo.getId(), new ArrayList<>()));
        }
        return list;
    }

    /**
     * 查询分页列表之后对列表数据的处理
     */
    private List<OrderInputListVo> handlerListAfter(List<OrderInputListVo> list) {
        if (CollUtil.isEmpty(list)) {
            return list;
        }
        Map<Long, List<OrderInputListProductVo>> orderIdMatchProduct = baseMapper.getBoxProductList(list.stream().map(OrderInputListVo::getId).collect(Collectors.toList())).stream().collect(Collectors.groupingBy(OrderInputListProductVo::getOrderId));
        for (OrderInputListVo orderInputListVo : list) {
            //sku去重 sku相同的数量进行叠加
            orderInputListVo.setProductMap(
                    orderIdMatchProduct.getOrDefault(orderInputListVo.getId(), new ArrayList<>()).stream()
                            .collect(
                                    Collectors.groupingBy(
                                            OrderInputListProductVo::getSku,
                                            Collectors.summingInt(OrderInputListProductVo::getQuantity)
                                    )
                            )
            );
        }
        return list;
    }

    @Override
    public SingleResult<CommonBatchOperationVo> postToJdyInputOrder(InboundPostToJdyBo bo) {
        CommonBatchOperationVo vo = new CommonBatchOperationVo();
        //获取入库单id
        List<Long> inboundIdList = bo.getIds();
        if (CollUtil.isEmpty(inboundIdList)) {
            return SingleResult.success(vo);
        }
        Map<Long, OrderInputDo> idMatchDo = this.listByIds(inboundIdList).stream().collect(Collectors.toMap(OrderInputDo::getId, val -> val));
        //获取入库单id对应的箱子信息
        Map<Long, List<OrderBoxDo>> orderIdMatchBoxList = boxService.lambdaQuery()
                .in(OrderBoxDo::getOrderId, idMatchDo.keySet())
                .list().stream().collect(Collectors.groupingBy(OrderBoxDo::getOrderId));
        List<String> boxMarkNoList = orderIdMatchBoxList.values().stream().flatMap(List::stream).map(OrderBoxDo::getBoxMarkNumber).collect(Collectors.toList());
        List<String> boxTypeNoList = orderIdMatchBoxList.values().stream().flatMap(List::stream).map(OrderBoxDo::getBoxTypeNumber).collect(Collectors.toList());
        //箱唛号匹配箱子内产品信息
        Map<String, List<OrderBoxProductDo>> boxMarkNoMatchList = new HashMap<>();
        if (CollUtil.isNotEmpty(boxMarkNoList)) {
            boxMarkNoMatchList = boxProductService.lambdaQuery()
                    .in(OrderBoxProductDo::getBoxMarkNumber, boxMarkNoList)
                    .list().stream().collect(Collectors.groupingBy(OrderBoxProductDo::getBoxMarkNumber));
        }
        //箱类号匹配箱子内产品信息
        Map<String, List<OrderBoxProductDo>> boxTypeNoMatchList = new HashMap<>();
        if (CollUtil.isNotEmpty(boxTypeNoList)) {
            boxTypeNoMatchList = boxProductService.lambdaQuery()
                    .in(OrderBoxProductDo::getBoxTypeNumber, boxTypeNoList)
                    .list().stream().collect(Collectors.groupingBy(OrderBoxProductDo::getBoxTypeNumber));
        }
        //产品id列表
        Set<Long> productIdSet = new HashSet<>();
        if (CollUtil.isNotEmpty(boxMarkNoMatchList)) {
            productIdSet.addAll(boxMarkNoMatchList.values().stream().flatMap(List::stream).map(OrderBoxProductDo::getProductId).collect(Collectors.toSet()));
        }
        if (CollUtil.isNotEmpty(boxTypeNoMatchList)) {
            productIdSet.addAll(boxTypeNoMatchList.values().stream().flatMap(List::stream).map(OrderBoxProductDo::getProductId).collect(Collectors.toSet()));
        }
        //获取产品id列表对应的产品信息
        Map<Long, ProductDo> productIdMatchDo = productService.lambdaQuery()
                .in(ProductDo::getId, productIdSet)
                .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        for (Long id : inboundIdList) {
            OrderInputDo inputDo = idMatchDo.get(id);
            if (inputDo == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", "id");
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Inbound_Not_Exist));
                vo.getFailedList().add(map);
                vo.setFailed(vo.getFailed() + 1);
                continue;
            }
            if (SendInEnum.Pushed.getValue().equals(inputDo.getSendIn())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", inputDo.getSourceNo());
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Has_Been_Pushed));
                vo.getFailedList().add(map);
                vo.setFailed(vo.getFailed() + 1);
                continue;
            }
            if (OrderInputStatusEnum.zero.getValue().equals(inputDo.getStatus()) || OrderInputStatusEnum.five.getValue().equals(inputDo.getStatus())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", inputDo.getSourceNo());
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Status_Error_SendIn_Fail));
                vo.getFailedList().add(map);
                vo.setFailed(vo.getFailed() + 1);
                continue;
            }
            //推送入库单到jdy
            String errorMsg = this.proxyWarehousePushToJdy(inputDo, orderIdMatchBoxList.get(inputDo.getId()), boxMarkNoMatchList, boxTypeNoMatchList, productIdMatchDo);
            if (StrUtil.isNotBlank(errorMsg)) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", inputDo.getSourceNo());
                map.put("errorMsg", errorMsg);
                vo.getFailedList().add(map);
                vo.setFailed(vo.getFailed() + 1);
            } else {
                vo.setSuccess(vo.getSuccess() + 1);
            }
        }
        return SingleResult.success(vo);
    }

    /**
     * 代理仓提交状态直接推送筋斗云
     * 本地出库单推送筋斗云入库单新增
     */
    @Override
    @Transactional(noRollbackFor = Exception.class)
    public String proxyWarehousePushToJdy(OrderInputDo inputDo, List<OrderBoxDo> boxList, Map<String, List<OrderBoxProductDo>> boxMarkNoMatchList, Map<String, List<OrderBoxProductDo>> boxTypeNoMatchList, Map<Long, ProductDo> productIdMatchDo) {
        SingleResult addResult;
        try {
            String jdyToken = outboundService.getJdyToken();
            addResult = outboundService.postToJdy(
                    jdyToken,
                    "api/open/orderInput",
                    this.buildJdyInputOrder(inputDo, boxList, boxMarkNoMatchList, boxTypeNoMatchList, productIdMatchDo));
            if (Objects.isNull(addResult) || Objects.isNull(addResult.getData())) {
                throw new Exception(StringsUtil.createI18Message(SysConstant.Jdy_Input_Order_Add_Fail));
            }
            if ("500".equals(addResult.getCode())) {
                throw new Exception(addResult.getMessage());
            }
        } catch (Exception e) {
            log.error("推送筋斗云入库单失败：" + e.getMessage());
            this.lambdaUpdate()
                    .eq(OrderInputDo::getId, inputDo.getId())
                    .set(OrderInputDo::getSendIn, SendInEnum.PushFailed.getValue())
                    .set(OrderInputDo::getSendInFailMsg, e.getMessage())
                    .update();
            return e.getMessage();
        }
        if ("200".equals(addResult.getCode())) {
            this.lambdaUpdate()
                    .eq(OrderInputDo::getId, inputDo.getId())
                    .set(OrderInputDo::getSendIn, SendInEnum.Pushed.getValue())
                    .set(OrderInputDo::getSendInFailMsg, null)
                    .update();
        }
        return null;
    }

    @Override
    public List<String> batchUpdateInputStatus(InputUpdateStatusBo bo, Long whId, Long userId, String username) {
        List<Integer> statusList = Arrays.asList(OrderInputStatusEnum.one.getValue(), OrderInputStatusEnum.two.getValue(), OrderInputStatusEnum.three.getValue(), OrderInputStatusEnum.four.getValue());
        if (!statusList.contains(bo.getStatus()) || bo.getInputNoList().isEmpty()) {
            throw new BizException(SysConstant.Status_Error);
        }
        List<OrderInputDo> inputDoList = this.lambdaQuery()
//                .select(OrderInputDo::getId, OrderInputDo::getSourceNo)
                .in(OrderInputDo::getSourceNo, bo.getInputNoList())
                .eq(OrderInputDo::getWarehouseId, whId)
                .in(OrderInputDo::getStatus, statusList)
                .list();
        if (inputDoList.isEmpty()) {
            return Collections.emptyList();
        }
        List<Long> inputIdList = inputDoList.stream().map(OrderInputDo::getId).collect(Collectors.toList());
        this.lambdaUpdate()
                .in(OrderInputDo::getId, inputIdList)
                .eq(OrderInputDo::getWarehouseId, whId)
                .in(OrderInputDo::getStatus, statusList)
                .set(OrderInputDo::getStatus, bo.getStatus())
                .set(OrderInputStatusEnum.three.getValue().equals(bo.getStatus()), OrderInputDo::getReceiptTime, DateUtil.date())
                .set(OrderInputStatusEnum.four.getValue().equals(bo.getStatus()), OrderInputDo::getShelfTime, DateUtil.date())
                .update();
        //修改入库单进度
        if (OrderInputStatusEnum.three.getValue().equals(bo.getStatus())) {
            progressService.lambdaUpdate()
                    .in(ProgressDo::getBizNumber, inputIdList)
                    .eq(ProgressDo::getType, ProgressEnum.INBOUND.getTypeCode())
                    .eq(ProgressDo::getBizType, "收货")
                    .set(ProgressDo::getOperateBy, username)
                    .set(ProgressDo::getOperateTime, DateUtil.date())
                    .update();
        }
        if (OrderInputStatusEnum.four.getValue().equals(bo.getStatus())) {
            progressService.lambdaUpdate()
                    .in(ProgressDo::getBizNumber, inputIdList)
                    .eq(ProgressDo::getType, ProgressEnum.INBOUND.getTypeCode())
                    .eq(ProgressDo::getBizType, "上架")
                    .set(ProgressDo::getBizType, "上架完成")
                    .set(ProgressDo::getOperateTime, DateUtil.date())
                    .set(ProgressDo::getOperateBy, username)
                    .update();
        }
        inputDoList.forEach(orderInputDo -> {
            context.publishEvent(new InboundAddCostEvent(orderInputDo, 0));
        });
        //操作日志
        List<OperateRecordDo> operateRecordDos = new ArrayList<>();
        for (OrderInputDo order : inputDoList) {
            OperateRecordDo operateRecordDo = new OperateRecordDo(order.getId(), OperateTypeEnum.OrderInput.getValue(), SysConstant.platformOrder_Update_Status, userId, new Date(), OrderInputStatusEnum.getDesc(bo.getStatus()));
            operateRecordDos.add(operateRecordDo);
        }
        rabbitTemplate.convertAndSend(RabbitQueueConstant.CrossMall_CMOrder_OperateList, JSON.toJSONString(operateRecordDos));
        return inputDoList.stream().map(OrderInputDo::getSourceNo).collect(Collectors.toList());
    }

    @Override
    public Map<String, Boolean> judgeUpdateBoxProductInfo(JudgeUpdateBoxProductInfoBo bo) {
        if (CollUtil.isEmpty(bo.getBoxTypeNumberList())) {
            return Collections.emptyMap();
        }
        Map<String, Set<Long>> boxTypeNoMatchOrderIds = boxService.list(Wrappers.<OrderBoxDo>lambdaQuery()
                        .in(OrderBoxDo::getBoxTypeNumber, bo.getBoxTypeNumberList())
                        .select(OrderBoxDo::getOrderId, OrderBoxDo::getBoxTypeNumber))
                .stream().collect(Collectors.groupingBy(OrderBoxDo::getBoxTypeNumber, Collectors.mapping(OrderBoxDo::getOrderId, Collectors.toSet())));
        if (CollUtil.isEmpty(boxTypeNoMatchOrderIds)) {
            return Collections.emptyMap();
        }
        Map<Long, Integer> orderIdMatchStatus = this.lambdaQuery()
                .in(OrderInputDo::getId, boxTypeNoMatchOrderIds.values().stream().flatMap(Set::stream).collect(Collectors.toSet()))
                .select(OrderInputDo::getId, OrderInputDo::getStatus)
                .list().stream().collect(Collectors.toMap(OrderInputDo::getId, OrderInputDo::getStatus));
        if (CollUtil.isEmpty(orderIdMatchStatus)) {
            return Collections.emptyMap();
        }
        Map<String, Boolean> result = new HashMap<>();
        for (String boxTypeNumber : bo.getBoxTypeNumberList()) {
            //判断使用箱类的订单状态几何
//            boolean allMatch = boxTypeNoMatchOrderIds.get(boxTypeNumber).stream().allMatch(item -> Objects.equals(orderIdMatchStatus.get(item), OrderInputStatusEnum.zero.getValue()) || Objects.equals(orderIdMatchStatus.get(item), OrderInputStatusEnum.five.getValue()));//需要在入库单save内同步处理相同引用订单的产品数量需改input&order_box表
            Set<Long> refOrderIds = boxTypeNoMatchOrderIds.get(boxTypeNumber);
            result.put(boxTypeNumber, CollUtil.isEmpty(refOrderIds));
        }
        return result;
    }

    /**
     * 根据wms入库单生成筋斗云入库单
     *
     * @return jdy入库单实体
     */
    private JdyOrderInputAddReq buildJdyInputOrder(OrderInputDo inputDo, List<OrderBoxDo> boxList, Map<String, List<OrderBoxProductDo>> boxMarkNoMatchList, Map<String, List<OrderBoxProductDo>> boxTypeNoMatchList, Map<Long, ProductDo> productIdMatchDo) {
        JdyOrderInputAddReq jdyOrderInputAddReq = new JdyOrderInputAddReq();
        jdyOrderInputAddReq.setShopId(681L);
        jdyOrderInputAddReq.setStorageId(15L);
        jdyOrderInputAddReq.setTips(inputDo.getSourceNo());
        Date expectedArrivalDate = inputDo.getExpectedDate();
        if (Objects.nonNull(expectedArrivalDate)) {
            jdyOrderInputAddReq.setDeliveryDate(DateUtil.format(expectedArrivalDate, "yyyy-MM-dd"));
        }
        jdyOrderInputAddReq.setCarriageType(0L);
        jdyOrderInputAddReq.setSendType(0L);
        jdyOrderInputAddReq.setCarriageTrackNo(inputDo.getTrackingNumber());
        jdyOrderInputAddReq.setFbaTurn(0);
        Integer inboundType = inputDo.getInboundType();
        //处理后的箱子信息
        List<OrderBoxDo> processedBoxList = new ArrayList<>();
        if (InboundTypeEnum.one.getValue().equals(inboundType)) {
            processedBoxList = new ArrayList<>(
                    boxList.stream().collect(Collectors.toMap(key -> key.getBoxMarkNumber().substring(0, key.getBoxMarkNumber().lastIndexOf("-")), a -> a, (o1, o2) -> {
                        o1.setBoxTotalQty(Math.max(Integer.parseInt(o1.getBoxMarkNumber().substring(o1.getBoxMarkNumber().lastIndexOf("-") + 1)), Integer.parseInt(o2.getBoxMarkNumber().substring(o2.getBoxMarkNumber().lastIndexOf("-") + 1))));
                        return o1;
                    })).values()
            );
        } else if (InboundTypeEnum.two.getValue().equals(inboundType)) {
            processedBoxList = new ArrayList<>(
                    boxList.stream().collect(Collectors.toMap(OrderBoxDo::getBoxTypeNumber, a -> a, (o1, o2) -> {
                        o1.setBoxTotalQty(o1.getBoxTotalQty() + o2.getBoxTotalQty());
                        return o1;
                    })).values()
            );
        }
        List<JdyOrderInputPackageAdd> jdyPackageList = new ArrayList<>();
        for (OrderBoxDo boxDo : processedBoxList) {
            List<JdyOrderInputProductAdd> productDetailList = new ArrayList<>();
            List<OrderBoxProductDo> boxProductDoList = new ArrayList<>();
            if (InboundTypeEnum.one.getValue().equals(inboundType)) {
                boxProductDoList = boxMarkNoMatchList.get(boxDo.getBoxMarkNumber());
            } else if (InboundTypeEnum.two.getValue().equals(inboundType)) {
                boxProductDoList = boxTypeNoMatchList.get(boxDo.getBoxTypeNumber());
            }
            List<PackageSizeVo> packageSizeVoList = new ArrayList<>();
            for (OrderBoxProductDo boxProductDo : boxProductDoList) {
                JdyOrderInputProductAdd jdyProductAdd = new JdyOrderInputProductAdd();
                jdyProductAdd.setProductSku(boxProductDo.getSku());
                jdyProductAdd.setCount(boxProductDo.getPerBoxQty());
                productDetailList.add(jdyProductAdd);
                ProductDo productDo = productIdMatchDo.get(boxProductDo.getProductId());
                if (productDo == null) {
                    continue;
                }
                packageSizeVoList.add(
                        PackageSizeVo.builder()
                                .length(productDo.getWmsLength() == null ? productDo.getLength() : productDo.getWmsLength())
                                .width(productDo.getWmsWidth() == null ? productDo.getWidth() : productDo.getWmsWidth())
                                .height(productDo.getWmsHeight() == null ? productDo.getHeight() : productDo.getWmsHeight())
                                .weight(productDo.getWmsWeight() == null ? productDo.getWeight() : productDo.getWmsWeight())
                                .qty(boxProductDo.getPerBoxQty())
                                .build()
                );
            }
            JdyOrderInputPackageAdd jdyPackageAdd = new JdyOrderInputPackageAdd();
            jdyPackageAdd.setProductDetailList(productDetailList);
            jdyPackageAdd.setPackageCount(Objects.isNull(boxDo.getBoxTotalQty()) || boxDo.getBoxTotalQty().equals(0) ? 1 : boxDo.getBoxTotalQty());
            jdyPackageAdd.setBoxTypeNumber(boxDo.getCustomizeBarcode());
            TrialBoxReq trialBoxReq = this.calculatePackageSize(packageSizeVoList);
            jdyPackageAdd.setPackageLenght(Objects.nonNull(trialBoxReq) ? trialBoxReq.getLength().floatValue() : 0);
            jdyPackageAdd.setPackageWidth(Objects.nonNull(trialBoxReq) ? trialBoxReq.getWidth().floatValue() : 0);
            jdyPackageAdd.setPackageHeight(Objects.nonNull(trialBoxReq) ? trialBoxReq.getHeight().floatValue() : 0);
            jdyPackageAdd.setPackageWeight(Objects.nonNull(trialBoxReq) ? trialBoxReq.getWeight().floatValue() : 0);
            jdyPackageList.add(jdyPackageAdd);
        }
        jdyOrderInputAddReq.setPackageList(jdyPackageList);
        return jdyOrderInputAddReq;
    }

    public TrialBoxReq calculatePackageSize(List<PackageSizeVo> packageSizeVoList) {
        if (CollUtil.isEmpty(packageSizeVoList)) {
            return null;
        }
        TrialBoxReq boxReq = new TrialBoxReq();
        packageSizeVoList.stream().map(PackageSizeVo::getLength).max(BigDecimal::compareTo).ifPresent(item -> {
            boxReq.setLength(item);
//            boxReq.setLengthBs(item.divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
        });
        packageSizeVoList.stream().map(PackageSizeVo::getWidth).max(BigDecimal::compareTo).ifPresent(item -> {
            boxReq.setWidth(item);
//            boxReq.setWidthBs(item.divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
        });
        //所有相加
        BigDecimal height = packageSizeVoList.stream().map(i -> i.getHeight().multiply(BigDecimal.valueOf(i.getQty()))).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal weight = packageSizeVoList.stream().map(i -> i.getWeight().multiply(BigDecimal.valueOf(i.getQty()))).reduce(BigDecimal.ZERO, BigDecimal::add);
        boxReq.setHeight(height);
//        boxReq.setHeightBs(height.divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
        boxReq.setWeight(weight);
//        boxReq.setWeightBs(weight.multiply(BigDecimal.valueOf(2.2046)).setScale(3, RoundingMode.HALF_UP));
        return boxReq;
    }
}
